From: liangzhenying <867174124@qq.com> Date: Wed, 10 Jul 2024 08:46:19 +0000 (+0800) Subject: chatGpt语音新增sbc数据流接口识别 X-Git-Tag: deepseek~19 X-Git-Url: http://git.snicker.com.cn/sitemap.xml?a=commitdiff_plain;h=f2975e68094b342baa435946f0654691e34eb3bc;p=mirrors%2FMixSdk-iOS.git chatGpt语音新增sbc数据流接口识别 --- diff --git a/MixSdk/Example/Example.xcodeproj/project.pbxproj b/MixSdk/Example/Example.xcodeproj/project.pbxproj index c93f514..80de489 100644 --- a/MixSdk/Example/Example.xcodeproj/project.pbxproj +++ b/MixSdk/Example/Example.xcodeproj/project.pbxproj @@ -142,14 +142,12 @@ 18204BD22C193F9A00F94E69 /* Example */ = { isa = PBXGroup; children = ( + 186E75102C3E33EB0040671B /* Tools */, 18DB20412C213D600070D571 /* Record */, 181188582C1BE80B00C23FAB /* Resource */, 18204BD32C193F9A00F94E69 /* AppDelegate.swift */, 18204BD52C193F9A00F94E69 /* SceneDelegate.swift */, 18204BD72C193F9A00F94E69 /* ViewController.swift */, - 184FFC482C218FC4006F664D /* PlayerManager.swift */, - 184FFC4C2C2199E5006F664D /* SendToolBar.swift */, - 180255B92C3BE6BD005AFC5F /* SendRecordTooBar.swift */, 18B74A202C201CD600FABBA9 /* TtsViewController.swift */, 184FFC502C21A4EB006F664D /* TtsLongViewController.swift */, 18B74A232C201CE900FABBA9 /* SttViewController.swift */, @@ -188,6 +186,16 @@ name = Frameworks; sourceTree = ""; }; + 186E75102C3E33EB0040671B /* Tools */ = { + isa = PBXGroup; + children = ( + 184FFC482C218FC4006F664D /* PlayerManager.swift */, + 184FFC4C2C2199E5006F664D /* SendToolBar.swift */, + 180255B92C3BE6BD005AFC5F /* SendRecordTooBar.swift */, + ); + path = Tools; + sourceTree = ""; + }; 18DB20412C213D600070D571 /* Record */ = { isa = PBXGroup; children = ( diff --git a/MixSdk/Example/Example/ChatGptViewController.swift b/MixSdk/Example/Example/ChatGptViewController.swift index ed2095c..68e6366 100644 --- a/MixSdk/Example/Example/ChatGptViewController.swift +++ b/MixSdk/Example/Example/ChatGptViewController.swift @@ -25,16 +25,16 @@ class ChatGptViewController: UIViewController{ func setupViews(){ self.title = "ChatGpt" self.view.backgroundColor = .white - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "选择文件", style: .done, target: self, action: #selector(selectFileAction)) +// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "选择文件", style: .done, target: self, action: #selector(selectFileAction)) - let toolBar = SendToolBar() - toolBar.textField.text = "我马上大四毕业了,请列出2024年最火的10个行业,并且为我规划未来十年的工作安排" + let toolBar = SendRecordToolBar() + toolBar.textField.text = "深圳今天天气怎么样" toolBar.delegate = self view.addSubview(toolBar) toolBar.snp.makeConstraints { make in make.width.centerX.equalToSuperview() - make.height.equalTo(60) - make.bottom.equalToSuperview().inset(20) + make.height.equalTo(150) + make.bottom.equalToSuperview().inset(0) } textView.isEditable = false @@ -45,24 +45,58 @@ class ChatGptViewController: UIViewController{ make.bottom.equalTo(toolBar.snp.top) } } + +// @objc func selectFileAction() { +// view.makeToastActivity(.center) +// guard let path = Bundle.main.path(forResource: "tts", ofType: "mp3") else {return} +// MixSdk.shared.chatGpt.transcription(filePath: path) {[weak self] text, error in +// guard let self = self else {return} +// self.view.hideToastActivity() +// if let err = error{ +// self.view.makeToast(err.message) +// } +// else if let text = text{ +// textView.text += "[receive] " + text + "\n" +// let range = NSMakeRange(textView.text.count - 1, 1) +// textView.scrollRangeToVisible(range) +// } +// } +// } + // @objc func selectFileAction() { + // view.makeToastActivity(.center) + // guard let pcmPath = Bundle.main.path(forResource: "input", ofType: "msbc") else {return} + // let mp3Path = NSHomeDirectory() + "/Documents/2.mp3" + // print(mp3Path) + // + // let ressult = MSAudioConvert.sbcToMp3(sbcPath: pcmPath, mp3Path: mp3Path) + // print(ressult) + // view.hideToastActivity() + // } } -extension ChatGptViewController:SendToolBarDelegate{ - @objc func selectFileAction() { +extension ChatGptViewController:SendRecordToolBarDelegate{ + + func sendRecordToolBarStartRecord(_ toolBar: SendRecordToolBar) { view.makeToastActivity(.center) - guard let path = Bundle.main.path(forResource: "tts", ofType: "mp3") else {return} - MixSdk.shared.chatGpt.transcription(filePath: path, useModel: "") {[weak self] response, error in - guard let self = self else {return} + //1、开始 + //注意demo中的手机录音的语音编码是pcm,所以参数sbc为false,如果蓝牙设备传过来的语音数据是sbc,则参数sbcä¼ true。 + MixSdk.shared.chatGptTrans.start(sbc:false) + } + + func sendRecordToolBar(_ toolBar: SendRecordToolBar, record data: Data) { + //2、发送的语音流 + MixSdk.shared.chatGptTrans.sendVoice(data: data) + } + + func sendRecordToolBarStopRecord(_ toolBar: SendRecordToolBar) { + //3、停止, + MixSdk.shared.chatGptTrans.stop {[weak self] text, error in + guard let self = self else {return} self.view.hideToastActivity() - if let err = error{ -// self.view.makeToast(err.msg) - } - else{ - - } + toolBar.textField.text = text ?? "" } } - func sendToolBar(_ toolBar: SendToolBar, _ text: String) { + func sendRecordToolBar(_ toolBar: SendRecordToolBar, _ text: String) { textView.text += "[send] " + text + "\n" let range = NSMakeRange(textView.text.count - 1, 1) diff --git a/MixSdk/Example/Example/ChatViewController.swift b/MixSdk/Example/Example/ChatViewController.swift index 9dfc1dc..08d43fc 100644 --- a/MixSdk/Example/Example/ChatViewController.swift +++ b/MixSdk/Example/Example/ChatViewController.swift @@ -104,6 +104,7 @@ extension ChatViewController:SendToolBarDelegate{ textView.scrollRangeToVisible(range) let content = text view.makeToastActivity(.center) + // 发送chatgpt MixSdk.shared.chatGpt.send(content: content,useModel: "gpt-3.5-turbo") {[weak self] response, error in guard let self = self else {return} self.view.hideToastActivity() @@ -118,6 +119,7 @@ extension ChatViewController:SendToolBarDelegate{ } } else{ + // 发送其他平台 if MixSdk.shared.chat.connectState == .connected { MixSdk.shared.chat.send(content: text) textView.text += "[send] " + text + "\n" @@ -128,7 +130,6 @@ extension ChatViewController:SendToolBarDelegate{ else{ //已断开连接 textView.text += "已断开连接。。。" - let range = NSMakeRange(textView.text.count - 1, 1) textView.scrollRangeToVisible(range) } diff --git a/MixSdk/Example/Example/PlayerManager.swift b/MixSdk/Example/Example/Tools/PlayerManager.swift similarity index 100% rename from MixSdk/Example/Example/PlayerManager.swift rename to MixSdk/Example/Example/Tools/PlayerManager.swift diff --git a/MixSdk/Example/Example/SendRecordTooBar.swift b/MixSdk/Example/Example/Tools/SendRecordTooBar.swift similarity index 60% rename from MixSdk/Example/Example/SendRecordTooBar.swift rename to MixSdk/Example/Example/Tools/SendRecordTooBar.swift index 7cf752f..7acb379 100644 --- a/MixSdk/Example/Example/SendRecordTooBar.swift +++ b/MixSdk/Example/Example/Tools/SendRecordTooBar.swift @@ -11,13 +11,17 @@ import UIKit @objc protocol SendRecordToolBarDelegate { - func SendRecordToolBar(_ toolBar:SendRecordToolBar,_ text:String) + func sendRecordToolBar(_ toolBar:SendRecordToolBar,_ text:String) - @objc optional func SendRecordToolBar(_ toolBar:SendRecordToolBar,record data:Data) + func sendRecordToolBarStartRecord(_ toolBar:SendRecordToolBar) + + func sendRecordToolBar(_ toolBar:SendRecordToolBar,record data:Data) + + func sendRecordToolBarStopRecord(_ toolBar:SendRecordToolBar) } class SendRecordToolBar:UIView{ - let textField = UITextField() + let textField = UITextView() let sendBtn = UIButton() let voiceBtn = UIButton() let recordMamager = AiMouseRecordManager() @@ -39,11 +43,13 @@ class SendRecordToolBar:UIView{ make.right.equalToSuperview().inset(10) } - voiceBtn.setTitle("录音", for: .normal) - voiceBtn.setTitle("正在录音", for: .selected) + voiceBtn.setTitle("按住说话", for: .normal) + voiceBtn.setTitle("松开结束", for: .highlighted) voiceBtn.setTitleColor(.white, for: .normal) voiceBtn.backgroundColor = .blue - voiceBtn.addTarget(self, action: #selector(voiceAction), for: .touchUpInside) + let longTap = UILongPressGestureRecognizer(target: self, action: #selector(recordLongPressAction(press:))) + voiceBtn.addGestureRecognizer(longTap) + addSubview(voiceBtn) voiceBtn.snp.makeConstraints { make in make.width.equalTo(80) @@ -52,13 +58,15 @@ class SendRecordToolBar:UIView{ } textField.backgroundColor = .white - textField.placeholder = "请输入文字" +// textField.placeholder = "请输入文字" addSubview(textField) textField.snp.makeConstraints { make in make.left.equalToSuperview().inset(10) make.top.bottom.equalToSuperview().inset(5) make.right.equalTo(voiceBtn.snp.left).offset(-10) } + + setupRecordCallBack() } required init?(coder: NSCoder) { @@ -67,24 +75,25 @@ class SendRecordToolBar:UIView{ @objc func sendAction(){ if let text = textField.text { - self.delegate?.SendRecordToolBar(self,text) + self.delegate?.sendRecordToolBar(self,text) } } - @objc func voiceAction(){ - voiceBtn.isSelected = !voiceBtn.isSelected - if voiceBtn.isSelected { - print("开始录音") - recordMamager.start() - } - else{ - print("停止录音") - recordMamager.stop() - } - } + func setupRecordCallBack(){ recordMamager.recordDataCallBack = {[weak self] data in guard let self = self else {return} - self.delegate?.SendRecordToolBar?(self, record: data) + self.delegate?.sendRecordToolBar(self, record: data) } } + @objc func recordLongPressAction(press longTap:UILongPressGestureRecognizer){ + if longTap.state == .began { + print("开始录音") + self.delegate?.sendRecordToolBarStartRecord(self) + recordMamager.start() + }else if longTap.state == .ended { + print("停止录音") + self.delegate?.sendRecordToolBarStopRecord(self) + recordMamager.stop() + } + } } diff --git a/MixSdk/Example/Example/SendToolBar.swift b/MixSdk/Example/Example/Tools/SendToolBar.swift similarity index 55% rename from MixSdk/Example/Example/SendToolBar.swift rename to MixSdk/Example/Example/Tools/SendToolBar.swift index 662d986..195584a 100644 --- a/MixSdk/Example/Example/SendToolBar.swift +++ b/MixSdk/Example/Example/Tools/SendToolBar.swift @@ -10,15 +10,11 @@ import UIKit @objc protocol SendToolBarDelegate { func sendToolBar(_ toolBar:SendToolBar,_ text:String) - - @objc optional func sendToolBar(_ toolBar:SendToolBar,record data:Data) } class SendToolBar:UIView{ let textField = UITextField() let sendBtn = UIButton() - let voiceBtn = UIButton() - let recordMamager = AiMouseRecordManager() weak var delegate:SendToolBarDelegate? @@ -37,25 +33,13 @@ class SendToolBar:UIView{ make.right.equalToSuperview().inset(10) } - voiceBtn.setTitle("录音", for: .normal) - voiceBtn.setTitle("正在录音", for: .selected) - voiceBtn.setTitleColor(.white, for: .normal) - voiceBtn.backgroundColor = .blue - voiceBtn.addTarget(self, action: #selector(voiceAction), for: .touchUpInside) - addSubview(voiceBtn) - voiceBtn.snp.makeConstraints { make in - make.width.equalTo(80) - make.top.bottom.equalToSuperview().inset(5) - make.right.equalTo(sendBtn.snp.left).offset(-10) - } - textField.backgroundColor = .white textField.placeholder = "请输入文字" addSubview(textField) textField.snp.makeConstraints { make in make.left.equalToSuperview().inset(10) make.top.bottom.equalToSuperview().inset(5) - make.right.equalTo(voiceBtn.snp.left).offset(-10) + make.right.equalTo(sendBtn.snp.left).offset(-10) } } @@ -68,21 +52,5 @@ class SendToolBar:UIView{ self.delegate?.sendToolBar(self,text) } } - @objc func voiceAction(){ - voiceBtn.isSelected = !voiceBtn.isSelected - if voiceBtn.isSelected { - print("开始录音") - recordMamager.start() - } - else{ - print("停止录音") - recordMamager.stop() - } - } - func setupRecordCallBack(){ - recordMamager.recordDataCallBack = {[weak self] data in - guard let self = self else {return} - self.delegate?.sendToolBar?(self, record: data) - } - } + } diff --git a/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate b/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate index 370fbd7..4095b69 100644 Binary files a/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate and b/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj b/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj index 14d1da6..641f674 100644 --- a/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj +++ b/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj @@ -36,6 +36,12 @@ 18314A862C3CD24E0028CE50 /* MSUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 18314A842C3CD24E0028CE50 /* MSUtils.m */; }; 18314A872C3CD24E0028CE50 /* MSUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 18314A852C3CD24E0028CE50 /* MSUtils.h */; }; 18314A892C3CD55E0028CE50 /* MSAuthModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18314A882C3CD55E0028CE50 /* MSAuthModel.swift */; }; + 18314A902C3D13A60028CE50 /* AudioConvert.h in Headers */ = {isa = PBXBuildFile; fileRef = 18314A8E2C3D13A60028CE50 /* AudioConvert.h */; }; + 18314A912C3D13A60028CE50 /* AudioConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 18314A8F2C3D13A60028CE50 /* AudioConvert.m */; }; + 18314AA12C3D37EE0028CE50 /* lame.h in Headers */ = {isa = PBXBuildFile; fileRef = 18314A9C2C3D37ED0028CE50 /* lame.h */; }; + 18314AA22C3D37EE0028CE50 /* libmp3lame.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18314A9E2C3D37ED0028CE50 /* libmp3lame.a */; }; + 18314AA32C3D37EE0028CE50 /* AudioTransform.h in Headers */ = {isa = PBXBuildFile; fileRef = 18314A9F2C3D37ED0028CE50 /* AudioTransform.h */; }; + 18314AA42C3D37EE0028CE50 /* AudioTransform.m in Sources */ = {isa = PBXBuildFile; fileRef = 18314AA02C3D37ED0028CE50 /* AudioTransform.m */; }; 184FFC542C21B091006F664D /* MSAigcModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC532C21B091006F664D /* MSAigcModel.swift */; }; 18B74A1C2C1FD44A00FABBA9 /* MSTts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A1B2C1FD44A00FABBA9 /* MSTts.swift */; }; 18B74A1E2C1FDDB500FABBA9 /* MSChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A1D2C1FDDB400FABBA9 /* MSChat.swift */; }; @@ -214,6 +220,12 @@ 18314A842C3CD24E0028CE50 /* MSUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSUtils.m; sourceTree = ""; }; 18314A852C3CD24E0028CE50 /* MSUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSUtils.h; sourceTree = ""; }; 18314A882C3CD55E0028CE50 /* MSAuthModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAuthModel.swift; sourceTree = ""; }; + 18314A8E2C3D13A60028CE50 /* AudioConvert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioConvert.h; sourceTree = ""; }; + 18314A8F2C3D13A60028CE50 /* AudioConvert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioConvert.m; sourceTree = ""; }; + 18314A9C2C3D37ED0028CE50 /* lame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lame.h; sourceTree = ""; }; + 18314A9E2C3D37ED0028CE50 /* libmp3lame.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmp3lame.a; sourceTree = ""; }; + 18314A9F2C3D37ED0028CE50 /* AudioTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTransform.h; sourceTree = ""; }; + 18314AA02C3D37ED0028CE50 /* AudioTransform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioTransform.m; sourceTree = ""; }; 184FFC532C21B091006F664D /* MSAigcModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAigcModel.swift; sourceTree = ""; }; 18B74A1B2C1FD44A00FABBA9 /* MSTts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSTts.swift; sourceTree = ""; }; 18B74A1D2C1FDDB400FABBA9 /* MSChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSChat.swift; sourceTree = ""; }; @@ -371,6 +383,7 @@ 18C42C622C242202000D8860 /* libavutil.a in Frameworks */, 18132AF52C1C4C85002F6B52 /* libz.tbd in Frameworks */, 18C42C652C242203000D8860 /* libavformat.a in Frameworks */, + 18314AA22C3D37EE0028CE50 /* libmp3lame.a in Frameworks */, 18C42C682C242203000D8860 /* libswscale.a in Frameworks */, 18C42C662C242203000D8860 /* libavdevice.a in Frameworks */, 18C42C672C242203000D8860 /* libswresample.a in Frameworks */, @@ -430,10 +443,11 @@ 18204BBD2C193E3D00F94E69 /* MixSdk */ = { isa = PBXGroup; children = ( + 18204BF12C19488800F94E69 /* SwiftHTTP */, + 18314A982C3D37ED0028CE50 /* AudioTransform */, 18C42C692C2422C2000D8860 /* AudioDecode */, 18C42B5B2C242202000D8860 /* FFmpeg-iOS */, 181188302C1A8B5200C23FAB /* Tools */, - 18204BF12C19488800F94E69 /* SwiftHTTP */, 18204BBE2C193E3D00F94E69 /* MixSdk.h */, 18C42C762C24241A000D8860 /* MixSdk-Bridging-Header.h */, 18204BEF2C19457600F94E69 /* MSHttpManager.swift */, @@ -471,6 +485,49 @@ path = SwiftHTTP; sourceTree = ""; }; + 18314A982C3D37ED0028CE50 /* AudioTransform */ = { + isa = PBXGroup; + children = ( + 18314A992C3D37ED0028CE50 /* fat-lame */, + 18314A9F2C3D37ED0028CE50 /* AudioTransform.h */, + 18314AA02C3D37ED0028CE50 /* AudioTransform.m */, + ); + path = AudioTransform; + sourceTree = ""; + }; + 18314A992C3D37ED0028CE50 /* fat-lame */ = { + isa = PBXGroup; + children = ( + 18314A9A2C3D37ED0028CE50 /* include */, + 18314A9D2C3D37ED0028CE50 /* lib */, + ); + path = "fat-lame"; + sourceTree = ""; + }; + 18314A9A2C3D37ED0028CE50 /* include */ = { + isa = PBXGroup; + children = ( + 18314A9B2C3D37ED0028CE50 /* lame */, + ); + path = include; + sourceTree = ""; + }; + 18314A9B2C3D37ED0028CE50 /* lame */ = { + isa = PBXGroup; + children = ( + 18314A9C2C3D37ED0028CE50 /* lame.h */, + ); + path = lame; + sourceTree = ""; + }; + 18314A9D2C3D37ED0028CE50 /* lib */ = { + isa = PBXGroup; + children = ( + 18314A9E2C3D37ED0028CE50 /* libmp3lame.a */, + ); + path = lib; + sourceTree = ""; + }; 18C42B5B2C242202000D8860 /* FFmpeg-iOS */ = { isa = PBXGroup; children = ( @@ -686,6 +743,8 @@ 18C42C6C2C2422C2000D8860 /* AiAudioDecoder.h */, 18C42C6B2C2422C2000D8860 /* AiAudioDecoder.m */, 18C42C7A2C24270D000D8860 /* MSAudioConvert.swift */, + 18314A8E2C3D13A60028CE50 /* AudioConvert.h */, + 18314A8F2C3D13A60028CE50 /* AudioConvert.m */, ); path = AudioDecode; sourceTree = ""; @@ -719,6 +778,7 @@ 18C42C292C242202000D8860 /* cpu.h in Headers */, 18C42BED2C242202000D8860 /* hwcontext_cuda.h in Headers */, 18C42C042C242202000D8860 /* frame.h in Headers */, + 18314A902C3D13A60028CE50 /* AudioConvert.h in Headers */, 18C42C2F2C242202000D8860 /* imgutils.h in Headers */, 18C42C3C2C242202000D8860 /* avassert.h in Headers */, 18C42C212C242202000D8860 /* opt.h in Headers */, @@ -761,11 +821,13 @@ 18C42C0A2C242202000D8860 /* ffversion.h in Headers */, 18C42C362C242202000D8860 /* hdr_dynamic_metadata.h in Headers */, 18C42C4A2C242202000D8860 /* version.h in Headers */, + 18314AA12C3D37EE0028CE50 /* lame.h in Headers */, 18C42C0C2C242202000D8860 /* audio_fifo.h in Headers */, 18C42C0E2C242202000D8860 /* threadmessage.h in Headers */, 18C42C0D2C242202000D8860 /* tree.h in Headers */, 18C42C492C242202000D8860 /* avcodec.h in Headers */, 18C42C2D2C242202000D8860 /* encryption_info.h in Headers */, + 18314AA32C3D37EE0028CE50 /* AudioTransform.h in Headers */, 18C42C702C2422C2000D8860 /* AiAudioDecoder.h in Headers */, 18204BBF2C193E3D00F94E69 /* MixSdk.h in Headers */, 18C42BFF2C242202000D8860 /* avutil.h in Headers */, @@ -907,6 +969,7 @@ 1811883B2C1A8B5200C23FAB /* Logger.swift in Sources */, 18C42B3D2C22F2C4000D8860 /* MSTtsModel.swift in Sources */, 181188572C1BE24200C23FAB /* MSStt.swift in Sources */, + 18314A912C3D13A60028CE50 /* AudioConvert.m in Sources */, 18204BFD2C19488900F94E69 /* Operation.swift in Sources */, 1811883C2C1A8B5200C23FAB /* JSON.swift in Sources */, 181188402C1A8B5200C23FAB /* LRC.swift in Sources */, @@ -924,6 +987,7 @@ 181188392C1A8B5200C23FAB /* Date.swift in Sources */, 18132AEF2C1C25CC002F6B52 /* MSAigc.swift in Sources */, 18C42B4B2C23DA29000D8860 /* SbcToPcm.swift in Sources */, + 18314AA42C3D37EE0028CE50 /* AudioTransform.m in Sources */, 1811883A2C1A8B5200C23FAB /* Any.swift in Sources */, 180255B82C3BD881005AFC5F /* MSTranscription.swift in Sources */, 1811883E2C1A8B5200C23FAB /* UTF8.swift in Sources */, @@ -1091,6 +1155,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/MixSdk/FFmpeg-iOS/lib", + "$(PROJECT_DIR)/MixSdk/AudioTransform/fat-lame/lib", ); MACH_O_TYPE = staticlib; MARKETING_VERSION = 1.0; @@ -1133,6 +1198,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/MixSdk/FFmpeg-iOS/lib", + "$(PROJECT_DIR)/MixSdk/AudioTransform/fat-lame/lib", ); MACH_O_TYPE = staticlib; MARKETING_VERSION = 1.0; diff --git a/MixSdk/MixSdk/MixSdk/AudioDecode/AudioConvert.h b/MixSdk/MixSdk/MixSdk/AudioDecode/AudioConvert.h new file mode 100644 index 0000000..6597771 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/AudioDecode/AudioConvert.h @@ -0,0 +1,16 @@ +// +// AudioConvert.h +// MixSdk +// +// Created by incar on 2024/7/9. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AudioConvert : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/MixSdk/MixSdk/MixSdk/AudioDecode/AudioConvert.m b/MixSdk/MixSdk/MixSdk/AudioDecode/AudioConvert.m new file mode 100644 index 0000000..09ce4db --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/AudioDecode/AudioConvert.m @@ -0,0 +1,12 @@ +// +// AudioConvert.m +// MixSdk +// +// Created by incar on 2024/7/9. +// + +#import "AudioConvert.h" + +@implementation AudioConvert + +@end diff --git a/MixSdk/MixSdk/MixSdk/AudioDecode/MSAudioConvert.swift b/MixSdk/MixSdk/MixSdk/AudioDecode/MSAudioConvert.swift index 8f331d9..2f60d7c 100644 --- a/MixSdk/MixSdk/MixSdk/AudioDecode/MSAudioConvert.swift +++ b/MixSdk/MixSdk/MixSdk/AudioDecode/MSAudioConvert.swift @@ -30,4 +30,54 @@ public class MSAudioConvert{ } let _ = AiAudioDecoder.sharedInstance().decodeSBCData(data) } + + + /// pcm转mp3 + /// - Parameters: + /// - pcmPath: pcm文件路径 + /// - mp3Path: mp3文件路径 + /// - Returns: 转换结果 + public static func pcmToMp3(_ pcmPath:String,_ mp3Path:String)->Bool { + let result = AudioTransform.convertPcm(pcmPath, toMp3: mp3Path) + return result + } + + /// sbc转mp3 + /// - Parameters: + /// - pcmPath: sbc文件路径 + /// - mp3Path: mp3文件路径 + /// - Returns: 转换结果 + public static func sbcToMp3(sbcPath:String,mp3Path:String)->Bool { + guard let sbcData = MSTool.readFile(from: sbcPath) else{ + return false + } + return sbcToMp3(sbcData:sbcData, mp3Path:mp3Path) + } + + + public static func sbcToMp3(sbcData:Data,mp3Path:String)->Bool { + let pcmData = self.sbcTopcm(sbcData) + + let pcmPath = NSTemporaryDirectory() + "temp.pcm" + if self.saveToFile(data: pcmData, path: pcmPath){ + let result = AudioTransform.convertPcm(pcmPath, toMp3: mp3Path) + return result + } + else{ + return false + } + } + + static func saveToFile(data:Data,path:String)->Bool{ + let url = URL(fileURLWithPath: path) + do { + try data.write(to: url, options: .atomic) + return true + } + catch{ + Logger.log(error) + return false + } + } } + diff --git a/MixSdk/MixSdk/MixSdk/AudioTransform/AudioTransform.h b/MixSdk/MixSdk/MixSdk/AudioTransform/AudioTransform.h new file mode 100644 index 0000000..95e5771 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/AudioTransform/AudioTransform.h @@ -0,0 +1,17 @@ +// +// AudioTransform.h +// ShangWuMiao +// +// Created by ju on 2017/7/19. +// Copyright © 2017å¹´ ju. All rights reserved. +// + +#import + +@interface AudioTransform : NSObject + +//+ (BOOL)transformToMp3FromPath:(NSString *)originalPath +// toMp3Path:(NSString *)mp3Path; + ++ (BOOL)convertPcm:(NSString *)pcmPath toMp3:(NSString *)mp3Path; +@end diff --git a/MixSdk/MixSdk/MixSdk/AudioTransform/AudioTransform.m b/MixSdk/MixSdk/MixSdk/AudioTransform/AudioTransform.m new file mode 100644 index 0000000..6f22807 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/AudioTransform/AudioTransform.m @@ -0,0 +1,119 @@ +// +// AudioTransform.m +// ShangWuMiao +// +// Created by ju on 2017/7/19. +// Copyright © 2017å¹´ ju. All rights reserved. +// + +#import "AudioTransform.h" +#import +#import "lame.h" + +// 采样率 +typedef NS_ENUM(NSInteger, AudioSample) { + AudioSampleRateLow = 8000, + AudioSampleRateMedium = 44100, //音频CD采样率 + AudioSampleRateHigh = 96000 +}; + +@implementation AudioTransform + ++ (BOOL)transformToMp3FromPath:(NSString *)originalPath + toMp3Path:(NSString *)mp3Path{ + @try { + int read, write; + + FILE *pcm = fopen([originalPath cStringUsingEncoding:1], "rb"); //source + fseek(pcm, 4*1024, SEEK_CUR); //skip file header + FILE *mp3 = fopen([mp3Path cStringUsingEncoding:1], "wb"); //output + + const int PCM_SIZE = 8192; + const int MP3_SIZE = 8192; + short int pcm_buffer[PCM_SIZE*2]; + unsigned char mp3_buffer[MP3_SIZE]; + + lame_t lame = lame_init(); + lame_set_in_samplerate(lame, 16000); + lame_set_num_channels(lame, 1); //声道,不设置默认为双声道 + lame_set_VBR(lame, vbr_default); + lame_init_params(lame); + + do { + read = (int)fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm); + if (read == 0) + write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); + else + write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); + + fwrite(mp3_buffer, write, 1, mp3); + + } while (read != 0); + + lame_close(lame); + fclose(mp3); + fclose(pcm); + } + @catch (NSException *exception) { + NSLog(@"%@",[exception description]); + return false; + } + @finally { + return true; + } + +} + +/*** + * pcm 文件转mp3文件 + */ ++ (BOOL)convertPcm:(NSString *)pcmPath toMp3:(NSString *)mp3Path { + @try { + FILE *fpcm = fopen([pcmPath cStringUsingEncoding:NSASCIIStringEncoding], "rb"); + if (fpcm == NULL) { + return false; + } +// fseek(fpcm, 1024*4, SEEK_CUR); //跳过源文件的信息头,不然在开头会有爆破音 + FILE *fmp3 = fopen([mp3Path cStringUsingEncoding:NSASCIIStringEncoding], "wb"); + + int channelCount = 1; // 声道数, 跟录音时配置一样的 + + lame_t lame = lame_init(); + lame_set_in_samplerate(lame, 16000); //设置采样率, 需要跟录音时的采样率相同 + lame_set_num_channels(lame, channelCount); //声道,不设置默认为双声道 + lame_set_VBR(lame, vbr_default); +// lame_set_mode(lame, 0); + lame_set_quality(lame, 2); + lame_init_params(lame); + + const int PCM_SIZE = 8192;// + const int MP3_SIZE = 8192; // + short int pcm_buffer[PCM_SIZE*channelCount]; + unsigned char mp3_buffer[MP3_SIZE]; + + int read, write; + do { + read = fread(pcm_buffer, channelCount*sizeof(short int), PCM_SIZE, fpcm); + if (read == 0) { + write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); + } else { + if (channelCount == 1) { + write = lame_encode_buffer(lame, pcm_buffer, NULL, read, mp3_buffer, MP3_SIZE); // 单声道音频转码 + } else { + write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); // 多声道音频转码 + } + } + fwrite(mp3_buffer, write, 1, fmp3); + } while (read != 0); + lame_mp3_tags_fid(lame, fmp3); + lame_close(lame); + fclose(fmp3); + fclose(fpcm); + } @catch (NSException *exception) { + NSLog(@"catch exception, %@", exception); + return false; + } @finally { + return true; + } +} +@end diff --git a/MixSdk/MixSdk/MixSdk/AudioTransform/fat-lame/include/lame/lame.h b/MixSdk/MixSdk/MixSdk/AudioTransform/fat-lame/include/lame/lame.h new file mode 100644 index 0000000..791d491 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/AudioTransform/fat-lame/include/lame/lame.h @@ -0,0 +1,1323 @@ +/* + * Interface to MP3 LAME encoding engine + * + * Copyright (c) 1999 Mark Taylor + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* $Id: lame.h,v 1.189.2.1 2012/01/08 23:49:58 robert Exp $ */ + +#ifndef LAME_LAME_H +#define LAME_LAME_H + +/* for size_t typedef */ +#include +/* for va_list typedef */ +#include +/* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */ +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef void (*lame_report_function)(const char *format, va_list ap); + +#if defined(WIN32) || defined(_WIN32) +#undef CDECL +#define CDECL __cdecl +#else +#define CDECL +#endif + +#define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1 + +typedef enum vbr_mode_e { + vbr_off=0, + vbr_mt, /* obsolete, same as vbr_mtrh */ + vbr_rh, + vbr_abr, + vbr_mtrh, + vbr_max_indicator, /* Don't use this! It's used for sanity checks. */ + vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */ +} vbr_mode; + + +/* MPEG modes */ +typedef enum MPEG_mode_e { + STEREO = 0, + JOINT_STEREO, + DUAL_CHANNEL, /* LAME doesn't supports this! */ + MONO, + NOT_SET, + MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ +} MPEG_mode; + +/* Padding types */ +typedef enum Padding_type_e { + PAD_NO = 0, + PAD_ALL, + PAD_ADJUST, + PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ +} Padding_type; + + + +/*presets*/ +typedef enum preset_mode_e { + /*values from 8 to 320 should be reserved for abr bitrates*/ + /*for abr I'd suggest to directly use the targeted bitrate as a value*/ + ABR_8 = 8, + ABR_320 = 320, + + V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/ + VBR_10 = 410, + V8 = 420, + VBR_20 = 420, + V7 = 430, + VBR_30 = 430, + V6 = 440, + VBR_40 = 440, + V5 = 450, + VBR_50 = 450, + V4 = 460, + VBR_60 = 460, + V3 = 470, + VBR_70 = 470, + V2 = 480, + VBR_80 = 480, + V1 = 490, + VBR_90 = 490, + V0 = 500, + VBR_100 = 500, + + + + /*still there for compatibility*/ + R3MIX = 1000, + STANDARD = 1001, + EXTREME = 1002, + INSANE = 1003, + STANDARD_FAST = 1004, + EXTREME_FAST = 1005, + MEDIUM = 1006, + MEDIUM_FAST = 1007 +} preset_mode; + + +/*asm optimizations*/ +typedef enum asm_optimizations_e { + MMX = 1, + AMD_3DNOW = 2, + SSE = 3 +} asm_optimizations; + + +/* psychoacoustic model */ +typedef enum Psy_model_e { + PSY_GPSYCHO = 1, + PSY_NSPSYTUNE = 2 +} Psy_model; + + +/* buffer considerations */ +typedef enum buffer_constraint_e { + MDB_DEFAULT=0, + MDB_STRICT_ISO=1, + MDB_MAXIMUM=2 +} buffer_constraint; + + +struct lame_global_struct; +typedef struct lame_global_struct lame_global_flags; +typedef lame_global_flags *lame_t; + + + + +/*********************************************************************** + * + * The LAME API + * These functions should be called, in this order, for each + * MP3 file to be encoded. See the file "API" for more documentation + * + ***********************************************************************/ + + +/* + * REQUIRED: + * initialize the encoder. sets default for all encoder parameters, + * returns NULL if some malloc()'s failed + * otherwise returns pointer to structure needed for all future + * API calls. + */ +lame_global_flags * CDECL lame_init(void); +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* obsolete version */ +int CDECL lame_init_old(lame_global_flags *); +#endif + +/* + * OPTIONAL: + * set as needed to override defaults + */ + +/******************************************************************** + * input stream description + ***********************************************************************/ +/* number of samples. default = 2^32-1 */ +int CDECL lame_set_num_samples(lame_global_flags *, unsigned long); +unsigned long CDECL lame_get_num_samples(const lame_global_flags *); + +/* input sample rate in Hz. default = 44100hz */ +int CDECL lame_set_in_samplerate(lame_global_flags *, int); +int CDECL lame_get_in_samplerate(const lame_global_flags *); + +/* number of channels in input stream. default=2 */ +int CDECL lame_set_num_channels(lame_global_flags *, int); +int CDECL lame_get_num_channels(const lame_global_flags *); + +/* + scale the input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale(lame_global_flags *, float); +float CDECL lame_get_scale(const lame_global_flags *); + +/* + scale the channel 0 (left) input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale_left(lame_global_flags *, float); +float CDECL lame_get_scale_left(const lame_global_flags *); + +/* + scale the channel 1 (right) input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale_right(lame_global_flags *, float); +float CDECL lame_get_scale_right(const lame_global_flags *); + +/* + output sample rate in Hz. default = 0, which means LAME picks best value + based on the amount of compression. MPEG only allows: + MPEG1 32, 44.1, 48khz + MPEG2 16, 22.05, 24 + MPEG2.5 8, 11.025, 12 + (not used by decoding routines) +*/ +int CDECL lame_set_out_samplerate(lame_global_flags *, int); +int CDECL lame_get_out_samplerate(const lame_global_flags *); + + +/******************************************************************** + * general control parameters + ***********************************************************************/ +/* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */ +int CDECL lame_set_analysis(lame_global_flags *, int); +int CDECL lame_get_analysis(const lame_global_flags *); + +/* + 1 = write a Xing VBR header frame. + default = 1 + this variable must have been added by a Hungarian notation Windows programmer :-) +*/ +int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); +int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); + +/* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */ +int CDECL lame_set_decode_only(lame_global_flags *, int); +int CDECL lame_get_decode_only(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* 1=encode a Vorbis .ogg file. default=0 */ +/* DEPRECATED */ +int CDECL lame_set_ogg(lame_global_flags *, int); +int CDECL lame_get_ogg(const lame_global_flags *); +#endif + +/* + internal algorithm selection. True quality is determined by the bitrate + but this variable will effect quality by selecting expensive or cheap algorithms. + quality=0..9. 0=best (very slow). 9=worst. + recommended: 2 near-best quality, not too slow + 5 good quality, fast + 7 ok quality, really fast +*/ +int CDECL lame_set_quality(lame_global_flags *, int); +int CDECL lame_get_quality(const lame_global_flags *); + +/* + mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono + default: lame picks based on compression ration and input channels +*/ +int CDECL lame_set_mode(lame_global_flags *, MPEG_mode); +MPEG_mode CDECL lame_get_mode(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* + mode_automs. Use a M/S mode with a switching threshold based on + compression ratio + DEPRECATED +*/ +int CDECL lame_set_mode_automs(lame_global_flags *, int); +int CDECL lame_get_mode_automs(const lame_global_flags *); +#endif + +/* + force_ms. Force M/S for all frames. For testing only. + default = 0 (disabled) +*/ +int CDECL lame_set_force_ms(lame_global_flags *, int); +int CDECL lame_get_force_ms(const lame_global_flags *); + +/* use free_format? default = 0 (disabled) */ +int CDECL lame_set_free_format(lame_global_flags *, int); +int CDECL lame_get_free_format(const lame_global_flags *); + +/* perform ReplayGain analysis? default = 0 (disabled) */ +int CDECL lame_set_findReplayGain(lame_global_flags *, int); +int CDECL lame_get_findReplayGain(const lame_global_flags *); + +/* decode on the fly. Search for the peak sample. If the ReplayGain + * analysis is enabled then perform the analysis on the decoded data + * stream. default = 0 (disabled) + * NOTE: if this option is set the build-in decoder should not be used */ +int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int); +int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* DEPRECATED: now does the same as lame_set_findReplayGain() + default = 0 (disabled) */ +int CDECL lame_set_ReplayGain_input(lame_global_flags *, int); +int CDECL lame_get_ReplayGain_input(const lame_global_flags *); + +/* DEPRECATED: now does the same as + lame_set_decode_on_the_fly() && lame_set_findReplayGain() + default = 0 (disabled) */ +int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int); +int CDECL lame_get_ReplayGain_decode(const lame_global_flags *); + +/* DEPRECATED: now does the same as lame_set_decode_on_the_fly() + default = 0 (disabled) */ +int CDECL lame_set_findPeakSample(lame_global_flags *, int); +int CDECL lame_get_findPeakSample(const lame_global_flags *); +#endif + +/* counters for gapless encoding */ +int CDECL lame_set_nogap_total(lame_global_flags*, int); +int CDECL lame_get_nogap_total(const lame_global_flags*); + +int CDECL lame_set_nogap_currentindex(lame_global_flags* , int); +int CDECL lame_get_nogap_currentindex(const lame_global_flags*); + + +/* + * OPTIONAL: + * Set printf like error/debug/message reporting functions. + * The second argument has to be a pointer to a function which looks like + * void my_debugf(const char *format, va_list ap) + * { + * (void) vfprintf(stdout, format, ap); + * } + * If you use NULL as the value of the pointer in the set function, the + * lame buildin function will be used (prints to stderr). + * To quiet any output you have to replace the body of the example function + * with just "return;" and use it in the set function. + */ +int CDECL lame_set_errorf(lame_global_flags *, lame_report_function); +int CDECL lame_set_debugf(lame_global_flags *, lame_report_function); +int CDECL lame_set_msgf (lame_global_flags *, lame_report_function); + + + +/* set one of brate compression ratio. default is compression ratio of 11. */ +int CDECL lame_set_brate(lame_global_flags *, int); +int CDECL lame_get_brate(const lame_global_flags *); +int CDECL lame_set_compression_ratio(lame_global_flags *, float); +float CDECL lame_get_compression_ratio(const lame_global_flags *); + + +int CDECL lame_set_preset( lame_global_flags* gfp, int ); +int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int ); + + + +/******************************************************************** + * frame params + ***********************************************************************/ +/* mark as copyright. default=0 */ +int CDECL lame_set_copyright(lame_global_flags *, int); +int CDECL lame_get_copyright(const lame_global_flags *); + +/* mark as original. default=1 */ +int CDECL lame_set_original(lame_global_flags *, int); +int CDECL lame_get_original(const lame_global_flags *); + +/* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */ +int CDECL lame_set_error_protection(lame_global_flags *, int); +int CDECL lame_get_error_protection(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */ +int CDECL lame_set_padding_type(lame_global_flags *, Padding_type); +Padding_type CDECL lame_get_padding_type(const lame_global_flags *); +#endif + +/* MP3 'private extension' bit Meaningless. default=0 */ +int CDECL lame_set_extension(lame_global_flags *, int); +int CDECL lame_get_extension(const lame_global_flags *); + +/* enforce strict ISO compliance. default=0 */ +int CDECL lame_set_strict_ISO(lame_global_flags *, int); +int CDECL lame_get_strict_ISO(const lame_global_flags *); + + +/******************************************************************** + * quantization/noise shaping + ***********************************************************************/ + +/* disable the bit reservoir. For testing only. default=0 */ +int CDECL lame_set_disable_reservoir(lame_global_flags *, int); +int CDECL lame_get_disable_reservoir(const lame_global_flags *); + +/* select a different "best quantization" function. default=0 */ +int CDECL lame_set_quant_comp(lame_global_flags *, int); +int CDECL lame_get_quant_comp(const lame_global_flags *); +int CDECL lame_set_quant_comp_short(lame_global_flags *, int); +int CDECL lame_get_quant_comp_short(const lame_global_flags *); + +int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/ +int CDECL lame_get_experimentalX(const lame_global_flags *); + +/* another experimental option. for testing only */ +int CDECL lame_set_experimentalY(lame_global_flags *, int); +int CDECL lame_get_experimentalY(const lame_global_flags *); + +/* another experimental option. for testing only */ +int CDECL lame_set_experimentalZ(lame_global_flags *, int); +int CDECL lame_get_experimentalZ(const lame_global_flags *); + +/* Naoki's psycho acoustic model. default=0 */ +int CDECL lame_set_exp_nspsytune(lame_global_flags *, int); +int CDECL lame_get_exp_nspsytune(const lame_global_flags *); + +void CDECL lame_set_msfix(lame_global_flags *, double); +float CDECL lame_get_msfix(const lame_global_flags *); + + +/******************************************************************** + * VBR control + ***********************************************************************/ +/* Types of VBR. default = vbr_off = CBR */ +int CDECL lame_set_VBR(lame_global_flags *, vbr_mode); +vbr_mode CDECL lame_get_VBR(const lame_global_flags *); + +/* VBR quality level. 0=highest 9=lowest */ +int CDECL lame_set_VBR_q(lame_global_flags *, int); +int CDECL lame_get_VBR_q(const lame_global_flags *); + +/* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */ +int CDECL lame_set_VBR_quality(lame_global_flags *, float); +float CDECL lame_get_VBR_quality(const lame_global_flags *); + +/* Ignored except for VBR=vbr_abr (ABR mode) */ +int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *); + +int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *); + +int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *); + +/* + 1=strictly enforce VBR_min_bitrate. Normally it will be violated for + analog silence +*/ +int CDECL lame_set_VBR_hard_min(lame_global_flags *, int); +int CDECL lame_get_VBR_hard_min(const lame_global_flags *); + +/* for preset */ +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +int CDECL lame_set_preset_expopts(lame_global_flags *, int); +#endif + +/******************************************************************** + * Filtering control + ***********************************************************************/ +/* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */ +int CDECL lame_set_lowpassfreq(lame_global_flags *, int); +int CDECL lame_get_lowpassfreq(const lame_global_flags *); +/* width of transition band, in Hz. Default = one polyphase filter band */ +int CDECL lame_set_lowpasswidth(lame_global_flags *, int); +int CDECL lame_get_lowpasswidth(const lame_global_flags *); + +/* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */ +int CDECL lame_set_highpassfreq(lame_global_flags *, int); +int CDECL lame_get_highpassfreq(const lame_global_flags *); +/* width of transition band, in Hz. Default = one polyphase filter band */ +int CDECL lame_set_highpasswidth(lame_global_flags *, int); +int CDECL lame_get_highpasswidth(const lame_global_flags *); + + +/******************************************************************** + * psycho acoustics and other arguments which you should not change + * unless you know what you are doing + ***********************************************************************/ + +/* only use ATH for masking */ +int CDECL lame_set_ATHonly(lame_global_flags *, int); +int CDECL lame_get_ATHonly(const lame_global_flags *); + +/* only use ATH for short blocks */ +int CDECL lame_set_ATHshort(lame_global_flags *, int); +int CDECL lame_get_ATHshort(const lame_global_flags *); + +/* disable ATH */ +int CDECL lame_set_noATH(lame_global_flags *, int); +int CDECL lame_get_noATH(const lame_global_flags *); + +/* select ATH formula */ +int CDECL lame_set_ATHtype(lame_global_flags *, int); +int CDECL lame_get_ATHtype(const lame_global_flags *); + +/* lower ATH by this many db */ +int CDECL lame_set_ATHlower(lame_global_flags *, float); +float CDECL lame_get_ATHlower(const lame_global_flags *); + +/* select ATH adaptive adjustment type */ +int CDECL lame_set_athaa_type( lame_global_flags *, int); +int CDECL lame_get_athaa_type( const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* select the loudness approximation used by the ATH adaptive auto-leveling */ +int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int); +int CDECL lame_get_athaa_loudapprox( const lame_global_flags *); +#endif + +/* adjust (in dB) the point below which adaptive ATH level adjustment occurs */ +int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float); +float CDECL lame_get_athaa_sensitivity( const lame_global_flags* ); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* OBSOLETE: predictability limit (ISO tonality formula) */ +int CDECL lame_set_cwlimit(lame_global_flags *, int); +int CDECL lame_get_cwlimit(const lame_global_flags *); +#endif + +/* + allow blocktypes to differ between channels? + default: 0 for jstereo, 1 for stereo +*/ +int CDECL lame_set_allow_diff_short(lame_global_flags *, int); +int CDECL lame_get_allow_diff_short(const lame_global_flags *); + +/* use temporal masking effect (default = 1) */ +int CDECL lame_set_useTemporal(lame_global_flags *, int); +int CDECL lame_get_useTemporal(const lame_global_flags *); + +/* use temporal masking effect (default = 1) */ +int CDECL lame_set_interChRatio(lame_global_flags *, float); +float CDECL lame_get_interChRatio(const lame_global_flags *); + +/* disable short blocks */ +int CDECL lame_set_no_short_blocks(lame_global_flags *, int); +int CDECL lame_get_no_short_blocks(const lame_global_flags *); + +/* force short blocks */ +int CDECL lame_set_force_short_blocks(lame_global_flags *, int); +int CDECL lame_get_force_short_blocks(const lame_global_flags *); + +/* Input PCM is emphased PCM (for instance from one of the rarely + emphased CDs), it is STRONGLY not recommended to use this, because + psycho does not take it into account, and last but not least many decoders + ignore these bits */ +int CDECL lame_set_emphasis(lame_global_flags *, int); +int CDECL lame_get_emphasis(const lame_global_flags *); + + + +/************************************************************************/ +/* internal variables, cannot be set... */ +/* provided because they may be of use to calling application */ +/************************************************************************/ +/* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */ +int CDECL lame_get_version(const lame_global_flags *); + +/* encoder delay */ +int CDECL lame_get_encoder_delay(const lame_global_flags *); + +/* + padding appended to the input to make sure decoder can fully decode + all input. Note that this value can only be calculated during the + call to lame_encoder_flush(). Before lame_encoder_flush() has + been called, the value of encoder_padding = 0. +*/ +int CDECL lame_get_encoder_padding(const lame_global_flags *); + +/* size of MPEG frame */ +int CDECL lame_get_framesize(const lame_global_flags *); + +/* number of PCM samples buffered, but not yet encoded to mp3 data. */ +int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp ); + +/* + size (bytes) of mp3 data buffered, but not yet encoded. + this is the number of bytes which would be output by a call to + lame_encode_flush_nogap. NOTE: lame_encode_flush() will return + more bytes than this because it will encode the reamining buffered + PCM samples before flushing the mp3 buffers. +*/ +int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp ); + +/* number of frames encoded so far */ +int CDECL lame_get_frameNum(const lame_global_flags *); + +/* + lame's estimate of the total number of frames to be encoded + only valid if calling program set num_samples +*/ +int CDECL lame_get_totalframes(const lame_global_flags *); + +/* RadioGain value. Multiplied by 10 and rounded to the nearest. */ +int CDECL lame_get_RadioGain(const lame_global_flags *); + +/* AudiophileGain value. Multipled by 10 and rounded to the nearest. */ +int CDECL lame_get_AudiophileGain(const lame_global_flags *); + +/* the peak sample */ +float CDECL lame_get_PeakSample(const lame_global_flags *); + +/* Gain change required for preventing clipping. The value is correct only if + peak sample searching was enabled. If negative then the waveform + already does not clip. The value is multiplied by 10 and rounded up. */ +int CDECL lame_get_noclipGainChange(const lame_global_flags *); + +/* user-specified scale factor required for preventing clipping. Value is + correct only if peak sample searching was enabled and no user-specified + scaling was performed. If negative then either the waveform already does + not clip or the value cannot be determined */ +float CDECL lame_get_noclipScale(const lame_global_flags *); + + + + + + + +/* + * REQUIRED: + * sets more internal configuration based on data provided above. + * returns -1 if something failed. + */ +int CDECL lame_init_params(lame_global_flags *); + + +/* + * OPTIONAL: + * get the version number, in a string. of the form: + * "3.63 (beta)" or just "3.63". + */ +const char* CDECL get_lame_version ( void ); +const char* CDECL get_lame_short_version ( void ); +const char* CDECL get_lame_very_short_version ( void ); +const char* CDECL get_psy_version ( void ); +const char* CDECL get_lame_url ( void ); +const char* CDECL get_lame_os_bitness ( void ); + +/* + * OPTIONAL: + * get the version numbers in numerical form. + */ +typedef struct { + /* generic LAME version */ + int major; + int minor; + int alpha; /* 0 if not an alpha version */ + int beta; /* 0 if not a beta version */ + + /* version of the psy model */ + int psy_major; + int psy_minor; + int psy_alpha; /* 0 if not an alpha version */ + int psy_beta; /* 0 if not a beta version */ + + /* compile time features */ + const char *features; /* Don't make assumptions about the contents! */ +} lame_version_t; +void CDECL get_lame_version_numerical(lame_version_t *); + + +/* + * OPTIONAL: + * print internal lame configuration to message handler + */ +void CDECL lame_print_config(const lame_global_flags* gfp); + +void CDECL lame_print_internals( const lame_global_flags *gfp); + + +/* + * input pcm data, output (maybe) mp3 frames. + * This routine handles all buffering, resampling and filtering for you. + * + * return code number of bytes output in mp3buf. Can be 0 + * -1: mp3buf was too small + * -2: malloc() problem + * -3: lame_init_params() not called + * -4: psycho acoustic problems + * + * The required mp3buf_size can be computed from num_samples, + * samplerate and encoding rate, but here is a worst case estimate: + * + * mp3buf_size in bytes = 1.25*num_samples + 7200 + * + * I think a tighter bound could be: (mt, March 2000) + * MPEG1: + * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 + * MPEG2: + * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 + * + * but test first if you use that! + * + * set mp3buf_size = 0 and LAME will not check if mp3buf_size is + * large enough. + * + * NOTE: + * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels + * will be averaged into the L channel before encoding only the L channel + * This will overwrite the data in buffer_l[] and buffer_r[]. + * +*/ +int CDECL lame_encode_buffer ( + lame_global_flags* gfp, /* global context handle */ + const short int buffer_l [], /* PCM data for left channel */ + const short int buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* + * as above, but input has L & R channel data interleaved. + * NOTE: + * num_samples = number of samples in the L (or R) + * channel, not the total number of samples in pcm[] + */ +int CDECL lame_encode_buffer_interleaved( + lame_global_flags* gfp, /* global context handlei */ + short int pcm[], /* PCM data for left and right + channel, interleaved */ + int num_samples, /* number of samples per channel, + _not_ number of samples in + pcm[] */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int mp3buf_size ); /* number of valid octets in this + stream */ + + +/* as lame_encode_buffer, but for 'float's. + * !! NOTE: !! data must still be scaled to be in the same range as + * short int, +/- 32768 + */ +int CDECL lame_encode_buffer_float( + lame_global_flags* gfp, /* global context handle */ + const float pcm_l [], /* PCM data for left channel */ + const float pcm_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* as lame_encode_buffer, but for 'float's. + * !! NOTE: !! data must be scaled to +/- 1 full scale + */ +int CDECL lame_encode_buffer_ieee_float( + lame_t gfp, + const float pcm_l [], /* PCM data for left channel */ + const float pcm_r [], /* PCM data for right channel */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); +int CDECL lame_encode_buffer_interleaved_ieee_float( + lame_t gfp, + const float pcm[], /* PCM data for left and right + channel, interleaved */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); + +/* as lame_encode_buffer, but for 'double's. + * !! NOTE: !! data must be scaled to +/- 1 full scale + */ +int CDECL lame_encode_buffer_ieee_double( + lame_t gfp, + const double pcm_l [], /* PCM data for left channel */ + const double pcm_r [], /* PCM data for right channel */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); +int CDECL lame_encode_buffer_interleaved_ieee_double( + lame_t gfp, + const double pcm[], /* PCM data for left and right + channel, interleaved */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); + +/* as lame_encode_buffer, but for long's + * !! NOTE: !! data must still be scaled to be in the same range as + * short int, +/- 32768 + * + * This scaling was a mistake (doesn't allow one to exploit full + * precision of type 'long'. Use lame_encode_buffer_long2() instead. + * + */ +int CDECL lame_encode_buffer_long( + lame_global_flags* gfp, /* global context handle */ + const long buffer_l [], /* PCM data for left channel */ + const long buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* Same as lame_encode_buffer_long(), but with correct scaling. + * !! NOTE: !! data must still be scaled to be in the same range as + * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1) + * + */ +int CDECL lame_encode_buffer_long2( + lame_global_flags* gfp, /* global context handle */ + const long buffer_l [], /* PCM data for left channel */ + const long buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* as lame_encode_buffer, but for int's + * !! NOTE: !! input should be scaled to the maximum range of 'int' + * If int is 4 bytes, then the values should range from + * +/- 2147483648. + * + * This routine does not (and cannot, without loosing precision) use + * the same scaling as the rest of the lame_encode_buffer() routines. + * + */ +int CDECL lame_encode_buffer_int( + lame_global_flags* gfp, /* global context handle */ + const int buffer_l [], /* PCM data for left channel */ + const int buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + + + + + +/* + * REQUIRED: + * lame_encode_flush will flush the intenal PCM buffers, padding with + * 0's to make sure the final frame is complete, and then flush + * the internal MP3 buffers, and thus may return a + * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long + * to hold all possible emitted data. + * + * will also write id3v1 tags (if any) into the bitstream + * + * return code = number of bytes output to mp3buf. Can be 0 + */ +int CDECL lame_encode_flush( + lame_global_flags * gfp, /* global context handle */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int size); /* number of valid octets in this stream */ + +/* + * OPTIONAL: + * lame_encode_flush_nogap will flush the internal mp3 buffers and pad + * the last frame with ancillary data so it is a complete mp3 frame. + * + * 'mp3buf' should be at least 7200 bytes long + * to hold all possible emitted data. + * + * After a call to this routine, the outputed mp3 data is complete, but + * you may continue to encode new PCM samples and write future mp3 data + * to a different file. The two mp3 files will play back with no gaps + * if they are concatenated together. + * + * This routine will NOT write id3v1 tags into the bitstream. + * + * return code = number of bytes output to mp3buf. Can be 0 + */ +int CDECL lame_encode_flush_nogap( + lame_global_flags * gfp, /* global context handle */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int size); /* number of valid octets in this stream */ + +/* + * OPTIONAL: + * Normally, this is called by lame_init_params(). It writes id3v2 and + * Xing headers into the front of the bitstream, and sets frame counters + * and bitrate histogram data to 0. You can also call this after + * lame_encode_flush_nogap(). + */ +int CDECL lame_init_bitstream( + lame_global_flags * gfp); /* global context handle */ + + + +/* + * OPTIONAL: some simple statistics + * a bitrate histogram to visualize the distribution of used frame sizes + * a stereo mode histogram to visualize the distribution of used stereo + * modes, useful in joint-stereo mode only + * 0: LR left-right encoded + * 1: LR-I left-right and intensity encoded (currently not supported) + * 2: MS mid-side encoded + * 3: MS-I mid-side and intensity encoded (currently not supported) + * + * attention: don't call them after lame_encode_finish + * suggested: lame_encode_flush -> lame_*_hist -> lame_close + */ + +void CDECL lame_bitrate_hist( + const lame_global_flags * gfp, + int bitrate_count[14] ); +void CDECL lame_bitrate_kbps( + const lame_global_flags * gfp, + int bitrate_kbps [14] ); +void CDECL lame_stereo_mode_hist( + const lame_global_flags * gfp, + int stereo_mode_count[4] ); + +void CDECL lame_bitrate_stereo_mode_hist ( + const lame_global_flags * gfp, + int bitrate_stmode_count[14][4] ); + +void CDECL lame_block_type_hist ( + const lame_global_flags * gfp, + int btype_count[6] ); + +void CDECL lame_bitrate_block_type_hist ( + const lame_global_flags * gfp, + int bitrate_btype_count[14][6] ); + +#if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0) +#else +/* + * OPTIONAL: + * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file + * pointer fid. These calls perform forward and backwards seeks, so make + * sure fid is a real file. Make sure lame_encode_flush has been called, + * and all mp3 data has been written to the file before calling this + * function. + * NOTE: + * if VBR tags are turned off by the user, or turned off by LAME because + * the output is not a regular file, this call does nothing + * NOTE: + * LAME wants to read from the file to skip an optional ID3v2 tag, so + * make sure you opened the file for writing and reading. + * NOTE: + * You can call lame_get_lametag_frame instead, if you want to insert + * the lametag yourself. +*/ +void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid); +#endif + +/* + * OPTIONAL: + * lame_get_lametag_frame copies the final LAME-tag into 'buffer'. + * The function returns the number of bytes copied into buffer, or + * the required buffer size, if the provided buffer is too small. + * Function failed, if the return value is larger than 'size'! + * Make sure lame_encode flush has been called before calling this function. + * NOTE: + * if VBR tags are turned off by the user, or turned off by LAME, + * this call does nothing and returns 0. + * NOTE: + * LAME inserted an empty frame in the beginning of mp3 audio data, + * which you have to replace by the final LAME-tag frame after encoding. + * In case there is no ID3v2 tag, usually this frame will be the very first + * data in your mp3 file. If you put some other leading data into your + * file, you'll have to do some bookkeeping about where to write this buffer. + */ +size_t CDECL lame_get_lametag_frame( + const lame_global_flags *, unsigned char* buffer, size_t size); + +/* + * REQUIRED: + * final call to free all remaining buffers + */ +int CDECL lame_close (lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* + * OBSOLETE: + * lame_encode_finish combines lame_encode_flush() and lame_close() in + * one call. However, once this call is made, the statistics routines + * will no longer work because the data will have been cleared, and + * lame_mp3_tags_fid() cannot be called to add data to the VBR header + */ +int CDECL lame_encode_finish( + lame_global_flags* gfp, + unsigned char* mp3buf, + int size ); +#endif + + + + + + +/********************************************************************* + * + * decoding + * + * a simple interface to mpglib, part of mpg123, is also included if + * libmp3lame is compiled with HAVE_MPGLIB + * + *********************************************************************/ + +struct hip_global_struct; +typedef struct hip_global_struct hip_global_flags; +typedef hip_global_flags *hip_t; + + +typedef struct { + int header_parsed; /* 1 if header was parsed and following data was + computed */ + int stereo; /* number of channels */ + int samplerate; /* sample rate */ + int bitrate; /* bitrate */ + int mode; /* mp3 frame type */ + int mode_ext; /* mp3 frame type */ + int framesize; /* number of samples per mp3 frame */ + + /* this data is only computed if mpglib detects a Xing VBR header */ + unsigned long nsamp; /* number of samples in mp3 file. */ + int totalframes; /* total number of frames in mp3 file */ + + /* this data is not currently computed by the mpglib routines */ + int framenum; /* frames decoded counter */ +} mp3data_struct; + +/* required call to initialize decoder */ +hip_t CDECL hip_decode_init(void); + +/* cleanup call to exit decoder */ +int CDECL hip_decode_exit(hip_t gfp); + +/* HIP reporting functions */ +void CDECL hip_set_errorf(hip_t gfp, lame_report_function f); +void CDECL hip_set_debugf(hip_t gfp, lame_report_function f); +void CDECL hip_set_msgf (hip_t gfp, lame_report_function f); + +/********************************************************************* + * input 1 mp3 frame, output (maybe) pcm data. + * + * nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r); + * + * input: + * len : number of bytes of mp3 data in mp3buf + * mp3buf[len] : mp3 data to be decoded + * + * output: + * nout: -1 : decoding error + * 0 : need more data before we can complete the decode + * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r + * pcm_l[nout] : left channel data + * pcm_r[nout] : right channel data + * + *********************************************************************/ +int CDECL hip_decode( hip_t gfp + , unsigned char * mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + ); + +/* same as hip_decode, and also returns mp3 header data */ +int CDECL hip_decode_headers( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + ); + +/* same as hip_decode, but returns at most one frame */ +int CDECL hip_decode1( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + ); + +/* same as hip_decode1, but returns at most one frame and mp3 header data */ +int CDECL hip_decode1_headers( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + ); + +/* same as hip_decode1_headers, but also returns enc_delay and enc_padding + from VBR Info tag, (-1 if no info tag was found) */ +int CDECL hip_decode1_headersB( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + , int *enc_delay + , int *enc_padding + ); + + + +/* OBSOLETE: + * lame_decode... functions are there to keep old code working + * but it is strongly recommended to replace calls by hip_decode... + * function calls, see above. + */ +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +int CDECL lame_decode_init(void); +int CDECL lame_decode( + unsigned char * mp3buf, + int len, + short pcm_l[], + short pcm_r[] ); +int CDECL lame_decode_headers( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data ); +int CDECL lame_decode1( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[] ); +int CDECL lame_decode1_headers( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data ); +int CDECL lame_decode1_headersB( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data, + int *enc_delay, + int *enc_padding ); +int CDECL lame_decode_exit(void); + +#endif /* obsolete lame_decode API calls */ + + +/********************************************************************* + * + * id3tag stuff + * + *********************************************************************/ + +/* + * id3tag.h -- Interface to write ID3 version 1 and 2 tags. + * + * Copyright (C) 2000 Don Melton. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +/* utility to obtain alphabetically sorted list of genre names with numbers */ +void CDECL id3tag_genre_list( + void (*handler)(int, const char *, void *), + void* cookie); + +void CDECL id3tag_init (lame_t gfp); + +/* force addition of version 2 tag */ +void CDECL id3tag_add_v2 (lame_t gfp); + +/* add only a version 1 tag */ +void CDECL id3tag_v1_only (lame_t gfp); + +/* add only a version 2 tag */ +void CDECL id3tag_v2_only (lame_t gfp); + +/* pad version 1 tag with spaces instead of nulls */ +void CDECL id3tag_space_v1 (lame_t gfp); + +/* pad version 2 tag with extra 128 bytes */ +void CDECL id3tag_pad_v2 (lame_t gfp); + +/* pad version 2 tag with extra n bytes */ +void CDECL id3tag_set_pad (lame_t gfp, size_t n); + +void CDECL id3tag_set_title(lame_t gfp, const char* title); +void CDECL id3tag_set_artist(lame_t gfp, const char* artist); +void CDECL id3tag_set_album(lame_t gfp, const char* album); +void CDECL id3tag_set_year(lame_t gfp, const char* year); +void CDECL id3tag_set_comment(lame_t gfp, const char* comment); + +/* return -1 result if track number is out of ID3v1 range + and ignored for ID3v1 */ +int CDECL id3tag_set_track(lame_t gfp, const char* track); + +/* return non-zero result if genre name or number is invalid + result 0: OK + result -1: genre number out of range + result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other' + but taken as-is for ID3v2 genre tag */ +int CDECL id3tag_set_genre(lame_t gfp, const char* genre); + +/* return non-zero result if field name is invalid */ +int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue); + +/* return non-zero result if image type is invalid */ +int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size); + +/* lame_get_id3v1_tag copies ID3v1 tag into buffer. + * Function returns number of bytes copied into buffer, or number + * of bytes rquired if buffer 'size' is too small. + * Function fails, if returned value is larger than 'size'. + * NOTE: + * This functions does nothing, if user/LAME disabled ID3v1 tag. + */ +size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size); + +/* lame_get_id3v2_tag copies ID3v2 tag into buffer. + * Function returns number of bytes copied into buffer, or number + * of bytes rquired if buffer 'size' is too small. + * Function fails, if returned value is larger than 'size'. + * NOTE: + * This functions does nothing, if user/LAME disabled ID3v2 tag. + */ +size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size); + +/* normaly lame_init_param writes ID3v2 tags into the audio stream + * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param + * to turn off this behaviour and get ID3v2 tag with above function + * write it yourself into your file. + */ +void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int); +int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp); + +/* experimental */ +int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text); + +/* experimental */ +int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* experimental */ +int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang, + unsigned short const *desc, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue); +#endif + +/* experimental */ +int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue); + +/* experimental */ +int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text); + + +/*********************************************************************** +* +* list of valid bitrates [kbps] & sample frequencies [Hz]. +* first index: 0: MPEG-2 values (sample frequencies 16...24 kHz) +* 1: MPEG-1 values (sample frequencies 32...48 kHz) +* 2: MPEG-2.5 values (sample frequencies 8...12 kHz) +***********************************************************************/ + +extern const int bitrate_table [3][16]; +extern const int samplerate_table [3][ 4]; + +/* access functions for use in DLL, global vars are not exported */ +int CDECL lame_get_bitrate(int mpeg_version, int table_index); +int CDECL lame_get_samplerate(int mpeg_version, int table_index); + + +/* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER + as well since lame_encode_buffer() also returns ID3v2 tag data */ +#define LAME_MAXALBUMART (128 * 1024) + +/* maximum size of mp3buffer needed if you encode at most 1152 samples for + each call to lame_encode_buffer. see lame_encode_buffer() below + (LAME_MAXMP3BUFFER is now obsolete) */ +#define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART) + + +typedef enum { + LAME_OKAY = 0, + LAME_NOERROR = 0, + LAME_GENERICERROR = -1, + LAME_NOMEM = -10, + LAME_BADBITRATE = -11, + LAME_BADSAMPFREQ = -12, + LAME_INTERNALERROR = -13, + + FRONTEND_READERROR = -80, + FRONTEND_WRITEERROR = -81, + FRONTEND_FILETOOLARGE = -82 + +} lame_errorcodes_t; + +#if defined(__cplusplus) +} +#endif +#endif /* LAME_LAME_H */ + diff --git a/MixSdk/MixSdk/MixSdk/AudioTransform/fat-lame/lib/libmp3lame.a b/MixSdk/MixSdk/MixSdk/AudioTransform/fat-lame/lib/libmp3lame.a new file mode 100644 index 0000000..bd397d2 Binary files /dev/null and b/MixSdk/MixSdk/MixSdk/AudioTransform/fat-lame/lib/libmp3lame.a differ diff --git a/MixSdk/MixSdk/MixSdk/MSAuth.swift b/MixSdk/MixSdk/MixSdk/MSAuth.swift index d8e78f4..83c2c00 100644 --- a/MixSdk/MixSdk/MixSdk/MSAuth.swift +++ b/MixSdk/MixSdk/MixSdk/MSAuth.swift @@ -17,7 +17,7 @@ public typealias MSLoginCallBack = (_ isSuccess: Bool,_ error:MSError?) -> Void /// 鉴权管理类 public class MSAuth { /// 是否已经鉴权 - public private(set) var authorized = true + public private(set) var authorized = false func getUrl(_ api:String)->String{ return "http://180.184.206.142/bz-api/" + api diff --git a/MixSdk/MixSdk/MixSdk/MSChatGpt.swift b/MixSdk/MixSdk/MixSdk/MSChatGpt.swift index b9c557c..7469d8d 100644 --- a/MixSdk/MixSdk/MixSdk/MSChatGpt.swift +++ b/MixSdk/MixSdk/MixSdk/MSChatGpt.swift @@ -7,22 +7,22 @@ import Foundation - /// ChatGpt public typealias MSChatGptCallBack = (_ response: MSChatGptModel?,_ error:MSChatGptError?) -> Void - +/// ChatGpt +public typealias MSChatGptTranscriptionCallBack = (_ text: String?,_ error:MSChatGptError?) -> Void /// 鉴权管理类 public class MSChatGpt { - var transcription = MSTranscription() + private var transcription = MSTranscription() - func getUrl()->String{ - return "https://api.openai.com/v1/chat/completions" + func getUrl(_ api:String)->String{ + return "https://api.openai.com/" + api } - public func transcription(filePath:String, useModel model:String,completeHandler: @escaping MSChatGptCallBack){ - guard let key = MixSdk.shared.aiKey else { + public func transcription(filePath:String,completeHandler: @escaping MSChatGptTranscriptionCallBack){ + guard let aiKey = MixSdk.shared.aiKey else { Logger.log("请设置key") completeHandler(nil,MSChatGptError()) return @@ -32,29 +32,59 @@ public class MSChatGpt { return } let fileUrl = URL(fileURLWithPath: filePath) - let params:[String : Any] = [ -// "model": model, - "model":"whisper-1", - "file": Upload(fileUrl: fileUrl) - ] +// let params:[String : Any] = [ +//// "model": model, +// "model":"whisper-1", +// "file": Upload(fileUrl: fileUrl) +// ] +// Logger.log(params) - let url = getUrl() - let head:[String:String] = [ - "Authorization":"Bearer \(key)", - "Content-Type":"multipart/form-data;boundary=WebAppBoundar" - ] - Logger.log(params) + let url = URL(string: getUrl("v1/audio/transcriptions"))! + var request = URLRequest(url: url) + + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Accept") + request.addValue("multipart/form-data", forHTTPHeaderField: "Content-Type") + request.setValue("Bearer \(aiKey)", forHTTPHeaderField: "Authorization") + request.addValue("gzip", forHTTPHeaderField: "Accept-Encoding") - self.transcription.transcribe(fileUrl) + // Audio file is sent to OpenAI as multipart form data. There is probably an easier way to do this with a built-in library + let boundary = UUID().uuidString + request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") + + var formData = Data() + formData.append("--\(boundary)\r\n".data(using: .utf8)!) + formData.append("Content-Disposition: form-data; name=\"file\"; filename=\"tts.mp3\"\r\n".data(using: .utf8)!) + formData.append("\r\n".data(using: .utf8)!) + formData.append(fileData) + formData.append("\r\n".data(using: .utf8)!) + + // This specifies the model to use "whisper-1" + formData.append("--\(boundary)\r\n".data(using: .utf8)!) + formData.append("Content-Disposition: form-data; name=\"model\"\r\n\r\nwhisper-1\r\n".data(using: .utf8)!) + + formData.append("--\(boundary)--\r\n".data(using: .utf8)!) + request.httpBody = formData + + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + DispatchQueue.main.sync { + let (dict,error) = MSHttpManager.receiveData(data,response,error) + if error != nil { + completeHandler(nil,MSChatGptError()) + return + } + guard let dict = dict else {return completeHandler(nil,MSChatGptError())} + completeHandler(dict["text"] as? String ?? "",nil) + } + } + task.resume() } - - - + /// 对话 /// - Parameters: /// - content: 内容 - /// - model: 使用的模型 + /// - model: 使用的模型,例如 gpt-3.5-turbo /// - completeHandler: 结果回调 public func send(content:String, useModel model:String,completeHandler: @escaping MSChatGptCallBack){ guard let key = MixSdk.shared.aiKey else { @@ -85,7 +115,7 @@ public class MSChatGpt { // ] // ] // ] - let url = getUrl() + let url = getUrl("v1/chat/completions") let head:[String:String] = ["Authorization":"Bearer \(key)"] Logger.log(params) Logger.log(head) @@ -112,3 +142,44 @@ public class MSChatGpt { } } } + +public class MSChatGptTranscription { + var voiceData = Data() + + var isSbc = true + + /// 开始 + public func start(sbc:Bool = true){ + voiceData = Data() + isSbc = sbc + } + + /// 停止 + public func stop(completeHandler: @escaping MSChatGptTranscriptionCallBack){ + let mp3Path = NSTemporaryDirectory() + "temp.mp3" + Logger.log(mp3Path) + var result = false + if isSbc { + result = MSAudioConvert.sbcToMp3(sbcData: voiceData, mp3Path: mp3Path) + } + else { + let pcmPath = NSTemporaryDirectory() + "temp.pcm" + if MSTool.saveToFile(data: voiceData, path: pcmPath){ + result = MSAudioConvert.pcmToMp3(pcmPath, mp3Path) + } + } + if result { + MixSdk.shared.chatGpt.transcription(filePath: mp3Path, completeHandler: completeHandler) + } + else{ + Logger.log("语音转换异常") + completeHandler(nil,MSChatGptError()) + } + } + + /// 发送语音数据,format sbc,采样率 16000 + /// - Parameter data: 语音数据 + public func sendVoice(data:Data){ + voiceData.append(data) + } +} diff --git a/MixSdk/MixSdk/MixSdk/MSHttpManager.swift b/MixSdk/MixSdk/MixSdk/MSHttpManager.swift index 4ab5b69..a78b596 100644 --- a/MixSdk/MixSdk/MixSdk/MSHttpManager.swift +++ b/MixSdk/MixSdk/MixSdk/MSHttpManager.swift @@ -21,53 +21,20 @@ class MSHttpManager{ return (dict,nil) } - // func request(_ url:URL){ - // // 创建URL对象 - // - // // 创建URLSessionDataTask来发送请求 - // let task = URLSession.shared.dataTask(with: url) { (data, response, error) in - // // 确保没有错误发生 - // if let error = error { - // print("Error: \(error)") - // return - // } - // - // // 确保我们得到了数据 - // guard let data = data else { - // print("未收到数据") - // return - // } - // - // // 尝试解析数据为JSON(如果服务器返回的是JSON) - // if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) { - // print(jsonObject) - // } else { - // print("JSON解析失败") - // } - // } - // - // // 启动任务 - // task.resume() - // } - // - // - // func post(_ url:URL){ - // var request = URLRequest(url: url) - // request.httpMethod = "POST" - // request.setValue("application/json", forHTTPHeaderField: "Content-Type") - // - // // 创建POST的数据(例如JSON) - // let postData = "{\"key\":\"value\"}".data(using: .utf8) - // request.httpBody = postData - // - // // 创建URLSessionDataTask来发送请求 - // let task = URLSession.shared.dataTask(with: request) { (data, response, error) in - // // 处理响应类似于GET请求 - // } - // - // // 启动任务 - // task.resume() - // } + static func receiveData(_ data:Data?, _ response:URLResponse?,_ error:Error?) ->(NSDictionary?,MSError?){ + if let _ = error { + Logger.log(response?.description ?? "") + return (nil,MSError.invalidRequest) + } + + guard let data = data,let dict = data.jsonObject() as? NSDictionary else{ + Logger.log(response?.description ?? "") + return (nil,MSError.parsingError) + } + Logger.log(data.string ?? "") + return (dict,nil) + } + } diff --git a/MixSdk/MixSdk/MixSdk/MSStt.swift b/MixSdk/MixSdk/MixSdk/MSStt.swift index 752897f..d295288 100644 --- a/MixSdk/MixSdk/MixSdk/MSStt.swift +++ b/MixSdk/MixSdk/MixSdk/MSStt.swift @@ -100,20 +100,12 @@ public class MSStt { request.httpBody = fileData let task = URLSession.shared.dataTask(with: request) { (data, response, error) in DispatchQueue.main.sync { + let (dict,error) = MSHttpManager.receiveData(data,response,error) if let error = error { - completeHandler(nil,.invalidRequest) - Logger.log("Error: \(error)") - return - } - guard let data = data else { - completeHandler(nil,.invalidRequest) - return - } - Logger.log(data.string ?? "") - guard let dict = data.jsonObject() as? NSDictionary else{ - completeHandler(nil,MSError.parsingError) + completeHandler(nil,error) return } + guard let dict = dict else {return completeHandler(nil,error)} if let status = dict["status"] as? Int,status == 0,let data = dict["data"] as? [String:Any], let result = data["result"] as? String{ completeHandler(result,nil) } @@ -175,7 +167,7 @@ public extension MSStt{ Logger.log("还未鉴权") return } - print("send \(pcmData)") + Logger.log("send \(pcmData)") socket.send(data: pcmData) } diff --git a/MixSdk/MixSdk/MixSdk/MixSdk-Bridging-Header.h b/MixSdk/MixSdk/MixSdk/MixSdk-Bridging-Header.h index f14df85..56b0997 100644 --- a/MixSdk/MixSdk/MixSdk/MixSdk-Bridging-Header.h +++ b/MixSdk/MixSdk/MixSdk/MixSdk-Bridging-Header.h @@ -7,3 +7,4 @@ #import "AudioDecode/AiAudioDecoder.h" #import "Tools/MSUtils.h" +#import "AudioTransform/AudioTransform.h" diff --git a/MixSdk/MixSdk/MixSdk/MixSdk.swift b/MixSdk/MixSdk/MixSdk/MixSdk.swift index d3c40b6..8491ac0 100644 --- a/MixSdk/MixSdk/MixSdk/MixSdk.swift +++ b/MixSdk/MixSdk/MixSdk/MixSdk.swift @@ -11,9 +11,13 @@ import Foundation // 访问密码: xRzzNg7l //let wsBaseURL = "ws://121.40.67.151:9099/" //let httpBaseURL = "http://121.40.67.151:9099/" + +//let wsBaseURL = "ws://118.145.158.207:80/" +//let httpBaseURL = "http://118.145.158.207:80/" + +let wsBaseURL = "ws://amp.raisound.com/" +let httpBaseURL = "https://amp.raisound.com/" let wsHeartBeatTime = 2.0 -let wsBaseURL = "ws://118.145.158.207:80/" -let httpBaseURL = "http://118.145.158.207:80/" /// Ai平台类型 @@ -99,6 +103,10 @@ public class MixSdk:NSObject { /// chatGpt public let chatGpt = MSChatGpt() + /// chatGpt语音识别 + public let chatGptTrans = MSChatGptTranscription() + + /// 设置平台 /// - Parameters: diff --git a/MixSdk/MixSdk/MixSdk/Tools/Logger.swift b/MixSdk/MixSdk/MixSdk/Tools/Logger.swift index 15beecd..cb0fa32 100644 --- a/MixSdk/MixSdk/MixSdk/Tools/Logger.swift +++ b/MixSdk/MixSdk/MixSdk/Tools/Logger.swift @@ -12,11 +12,35 @@ class Logger { static var isDebug: Bool = false fileprivate static let shared = Logger() - private init() { } + private var fileHandle:FileHandle? + private init() { + let path = getPath() + fileHandle = FileHandle(forWritingAtPath: path)! +// fileHandle?.seekToEndOfFile() + } fileprivate var dateString: String { Date().format(.full) } static var logs: [String] = [] + + + private func getPath()->String{ +// let path = NSHomeDirectory() + "/Documents/IOTBluetooth/" + "\(Date().format(.middle)).log" + let foldPath = NSHomeDirectory() + "/Documents/MixSdk/" + let fileManager = FileManager.default + if !fileManager.fileExists(atPath: foldPath) { + do { + try FileManager.default.createDirectory(atPath: foldPath, withIntermediateDirectories: true, attributes: nil) + } catch { + return "" + } + } + let path = foldPath + "\(Date().format(.middle)).log" + if !FileManager.default.fileExists(atPath: path) { + FileManager.default.createFile(atPath: path, contents: nil, attributes: nil) + } + return path + } } extension Logger { @@ -27,7 +51,19 @@ extension Logger { print(logs, separator: separator, terminator: terminator) let logString = Logger.shared.dateString + " [MixSdk-\(String(format: "%03d", line))]: " + logs.map({ "\($0)" }).joined(separator: separator) + terminator Self.logs.append(logString) + Logger.shared.saveLog(log: logString) } objc_sync_exit(Logger.shared) + + + } + + private func saveLog(log:String){ + fileHandle?.write(log.data(using: .utf8)!) +// if #available(iOS 13.0, *) { +// try? fileHandle.close() +// } else { +// // Fallback on earlier versions +// } } } diff --git a/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift b/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift index c09f993..bc82197 100644 --- a/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift +++ b/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift @@ -41,6 +41,18 @@ class MSTool{ } } + static func saveToFile(data:Data,path:String)->Bool{ + let url = URL(fileURLWithPath: path) + do { + try data.write(to: url, options: .atomic) + return true + } + catch{ + Logger.log(error) + return false + } + } + static func base64ToImage(_ base64String: String) -> UIImage? { if let data = Data(base64Encoded: base64String), let image = UIImage(data: data) { diff --git "a/document/MixSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" "b/document/MixSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" index 381e3df..a41fd09 100644 --- "a/document/MixSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" +++ "b/document/MixSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" @@ -2,10 +2,14 @@ ## 介绍 -- 当前版本:v1.0.5 -- 更新内容20240706: - 1. 新增chatGpt支持。 - 1. 新增对大话模型平台切换。 +- 当前版本:v1.0.6 + +- 更新内容20240710: + 1. 登录注册接口修改 + + 1. 新增chatGpt支持语音转写 + + ## 环境要求 @@ -40,7 +44,7 @@ MixSdk.shared.isDebug = true public class MSAuth { /// 是否已经鉴权 - public var authorized: Bool + public private(set) var authorized: Bool { get } /// 注册 /// - Parameters: @@ -49,7 +53,7 @@ public class MSAuth { /// - deviceId: 设备ID /// - sn: SN号 /// - completeHandler: 结果回调 - public func register(productKey: String, secretKey: String, deviceId: String, sn: String, completeHandler: @escaping MixSdk.MSRegisterCallBack) + public func register(productKey: String, secretKey: String, deviceId: String, completeHandler: @escaping MixSdk.MSRegisterCallBack) /// 登录 /// - Parameters: @@ -58,8 +62,9 @@ public class MSAuth { /// - deviceId: 设备ID /// - deviceSecrect: 注册返回的密码 /// - completeHandler: 结果回调 - public func login(productKey: String, secretKey: String, deviceId: String, deviceSecrect: String, completeHandler: @escaping MixSdk.MSLoginCallBack) + public func login(deviceId: String, password: String, nonce: String, completeHandler: @escaping MixSdk.MSLoginCallBack) } + ``` @@ -160,11 +165,28 @@ public class MSChatGpt { } ``` +## chatGpt语音转写文字 + +```swift +public class MSChatGptTranscription { + /// 开始 + public func start(sbc: Bool = true) + + /// 停止 + public func stop(completeHandler: @escaping MixSdk.MSChatGptTranscriptionCallBack) + + /// 发送语音数据,format sbc,采样率 16000 + /// - Parameter data: 语音数据 + public func sendVoice(data: Data) +} + +``` + ## 文本合成语音 -### 一句话识别 +### 语音文件转文字 ```swift extension MSStt { @@ -180,7 +202,7 @@ extension MSStt { ``` -### 实时转写 +### 实时数据流转文字 ```swift public class MSStt { @@ -274,7 +296,29 @@ public class MSAudioConvert { /// - data: sbc数据 /// - callBack: 转回回调,会回调多次, public static func sbcTopcm(_ data: Data, _ callBack: @escaping MixSdk.MSAudioConvertCallBack) + + /// pcm转mp3 + /// - Parameters: + /// - pcmPath: pcm文件路径 + /// - mp3Path: mp3文件路径 + /// - Returns: 转换结果 + public static func pcmToMp3(_ pcmPath: String, _ mp3Path: String) -> Bool + + /// sbc转mp3 + /// - Parameters: + /// - sbcPath: sbc文件路径 + /// - mp3Path: mp3文件路径 + /// - Returns: 转换结果 + public static func sbcToMp3(sbcPath: String, mp3Path: String) -> Bool + + /// sbc转mp3 + /// - Parameters: + /// - sbcData: sbc 语音数据 + /// - mp3Path: mp3文件路径 + /// - Returns: 转换结果 + public static func sbcToMp3(sbcData: Data, mp3Path: String) -> Bool } + ``` ### MixSdk @@ -298,26 +342,29 @@ public class MixSdk : NSObject { public var isDebug: Bool /// 鉴权 - public var auth: MixSdk.MSAuth + public let auth: MixSdk.MSAuth /// 翻译 - public var translate: MixSdk.MSTranslate + public let translate: MixSdk.MSTranslate /// 人工智能AIGC - public var aigc: MixSdk.MSAigc + public let aigc: MixSdk.MSAigc /// 大模型对话 - public var chat: MixSdk.MSChat + public let chat: MixSdk.MSChat /// 文字转语音 - public var tts: MixSdk.MSTts + public let tts: MixSdk.MSTts /// 语音转文字 - public var stt: MixSdk.MSStt - + public let stt: MixSdk.MSStt + /// chatGpt public let chatGpt: MixSdk.MSChatGpt + /// chatGpt语音识别 + public let chatGptTrans: MixSdk.MSChatGptTranscription + /// 设置平台 /// - Parameters: /// - platform: 使用的平台 @@ -330,8 +377,28 @@ public class MixSdk : NSObject { /// aikey public var aiKey: String? } + ``` +### MSRegisterModel + +```swift +/// 注册返回模型 +public class MSRegisterModel { + + /// 设备ID + public var deviceId: String + + /// 设备密码 + public var password: String + + /// 16个字符的随机字符串 + public var nonce: String +} +``` + + + ### MSError ```swift @@ -418,7 +485,7 @@ public class MSChatModel : Codable { } ``` -## MSChatGptModel +### MSChatGptModel ```swift /// 回复的消息体