JLBA202505130001_关于吉林银行新建AI智能培训系统的需求_完善代码规范
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
# 任东专用开发环境
|
||||
ENV = 'development.rd'
|
||||
# 'development.rd'
|
||||
|
||||
#VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
|
||||
VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
|
||||
|
||||
VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.32.154:9604'
|
||||
@@ -5,4 +5,4 @@
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
#Sun Mar 29 00:53:29 CST 2026
|
||||
sdk.dir=C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk
|
||||
sdk.dir=C\:\\Users\\1\\AppData\\Local\\Android\\Sdk
|
||||
|
||||
@@ -9,3 +9,5 @@ npm install @dcloudio/uni-uts-v1 http://25.12.10.69:8081/repository/npm/
|
||||
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
|
||||
JLBA202505130001_关于吉林银行新建AI智能培训系统的需求_
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"deploymentTarget": "35"
|
||||
}
|
||||
@@ -1,493 +0,0 @@
|
||||
import AVFoundation
|
||||
import DCloudUTSFoundation
|
||||
import Foundation
|
||||
import Swift
|
||||
@objc(UTSSDKModulesTyRecordingAudioStartRes)
|
||||
@objcMembers
|
||||
public class AudioStartRes : NSObject, UTSObject {
|
||||
public var recordId: String!
|
||||
public var sampleRate: NSNumber?
|
||||
public var startTime: NSNumber!
|
||||
public subscript(_ key: String) -> Any? {
|
||||
get {
|
||||
return utsSubscriptGetValue(key)
|
||||
}
|
||||
set {
|
||||
switch(key){
|
||||
case "recordId":
|
||||
self.recordId = try! utsSubscriptCheckValue(newValue)
|
||||
case "sampleRate":
|
||||
self.sampleRate = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "startTime":
|
||||
self.startTime = try! utsSubscriptCheckValue(newValue)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
public init(_ obj: UTSJSONObject) {
|
||||
self.recordId = obj["recordId"] as! String
|
||||
self.sampleRate = obj["sampleRate"] as! NSNumber?
|
||||
self.startTime = obj["startTime"] as! NSNumber
|
||||
}
|
||||
}
|
||||
@objc(UTSSDKModulesTyRecordingAudioFileRes)
|
||||
@objcMembers
|
||||
public class AudioFileRes : NSObject, UTSObject {
|
||||
public var filePath: String!
|
||||
public var fileSize: NSNumber!
|
||||
public var duration: NSNumber!
|
||||
public var format: String!
|
||||
public var endTime: NSNumber!
|
||||
public subscript(_ key: String) -> Any? {
|
||||
get {
|
||||
return utsSubscriptGetValue(key)
|
||||
}
|
||||
set {
|
||||
switch(key){
|
||||
case "filePath":
|
||||
self.filePath = try! utsSubscriptCheckValue(newValue)
|
||||
case "fileSize":
|
||||
self.fileSize = try! utsSubscriptCheckValue(newValue)
|
||||
case "duration":
|
||||
self.duration = try! utsSubscriptCheckValue(newValue)
|
||||
case "format":
|
||||
self.format = try! utsSubscriptCheckValue(newValue)
|
||||
case "endTime":
|
||||
self.endTime = try! utsSubscriptCheckValue(newValue)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
public init(_ obj: UTSJSONObject) {
|
||||
self.filePath = obj["filePath"] as! String
|
||||
self.fileSize = obj["fileSize"] as! NSNumber
|
||||
self.duration = obj["duration"] as! NSNumber
|
||||
self.format = obj["format"] as! String
|
||||
self.endTime = obj["endTime"] as! NSNumber
|
||||
}
|
||||
}
|
||||
public typealias AudioErrorCode = NSNumber
|
||||
public protocol AudioErrorRes : IUniError {
|
||||
var errCode: NSNumber { get set }
|
||||
}
|
||||
@objc(UTSSDKModulesTyRecordingStartAudioRecordOptions)
|
||||
@objcMembers
|
||||
public class StartAudioRecordOptions : NSObject, UTSObject {
|
||||
public var autoStopDuration: NSNumber?
|
||||
public var onFrame: ((_ frameData: Any) -> Void)?
|
||||
public var onVolume: ((_ volume: NSNumber) -> Void)?
|
||||
public var onStart: ((_ res: AudioStartRes) -> Void)?
|
||||
public var onError: ((_ res: AudioErrorRes) -> Void)?
|
||||
public var onStop: (() -> Void)?
|
||||
public var onFile: ((_ res: AudioFileRes) -> Void)?
|
||||
public subscript(_ key: String) -> Any? {
|
||||
get {
|
||||
return utsSubscriptGetValue(key)
|
||||
}
|
||||
set {
|
||||
switch(key){
|
||||
case "autoStopDuration":
|
||||
self.autoStopDuration = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "onFrame":
|
||||
self.onFrame = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "onVolume":
|
||||
self.onVolume = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "onStart":
|
||||
self.onStart = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "onError":
|
||||
self.onError = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "onStop":
|
||||
self.onStop = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
case "onFile":
|
||||
self.onFile = try! utsSubscriptCheckValueIfPresent(newValue)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
public init(_ obj: UTSJSONObject) {
|
||||
self.autoStopDuration = obj["autoStopDuration"] as! NSNumber?
|
||||
self.onFrame = obj["onFrame"] as! ((_ frameData: Any) -> Void)?
|
||||
self.onVolume = obj["onVolume"] as! ((_ volume: NSNumber) -> Void)?
|
||||
self.onStart = obj["onStart"] as! ((_ res: AudioStartRes) -> Void)?
|
||||
self.onError = obj["onError"] as! ((_ res: AudioErrorRes) -> Void)?
|
||||
self.onStop = obj["onStop"] as! (() -> Void)?
|
||||
self.onFile = obj["onFile"] as! ((_ res: AudioFileRes) -> Void)?
|
||||
}
|
||||
}
|
||||
public var UniErrorSubject = "ty-recording"
|
||||
public var AudioErrors = Map<AudioErrorCode, String>([
|
||||
[
|
||||
9010001,
|
||||
"麦克风权限未授予,请前往设置开启1"
|
||||
],
|
||||
[
|
||||
9010002,
|
||||
"麦克风权限被永久拒绝,请前往设置开启"
|
||||
],
|
||||
[
|
||||
9010003,
|
||||
"获取麦克风权限状态失败"
|
||||
],
|
||||
[
|
||||
9010010,
|
||||
"音频录制实例初始化失败"
|
||||
],
|
||||
[
|
||||
9010011,
|
||||
"音频录制实例未初始化,无法执行操作"
|
||||
],
|
||||
[
|
||||
9010012,
|
||||
"系统资源不足,无法创建音频录制实例"
|
||||
],
|
||||
[
|
||||
9010020,
|
||||
"音频采集读取数据失败"
|
||||
],
|
||||
[
|
||||
9010021,
|
||||
"音频采集被中断(麦克风被其他应用抢占)"
|
||||
],
|
||||
[
|
||||
9010022,
|
||||
"音频采集缓冲区无有效数据"
|
||||
],
|
||||
[
|
||||
9010023,
|
||||
"启动音频采集失败"
|
||||
],
|
||||
[
|
||||
9010030,
|
||||
"采集线程创建失败"
|
||||
],
|
||||
[
|
||||
9010031,
|
||||
"采集线程启动失败"
|
||||
],
|
||||
[
|
||||
9010032,
|
||||
"采集线程中断失败"
|
||||
],
|
||||
[
|
||||
9010033,
|
||||
"采集线程运行崩溃"
|
||||
],
|
||||
[
|
||||
9010040,
|
||||
"停止音频采集失败"
|
||||
],
|
||||
[
|
||||
9010041,
|
||||
"音频录制资源释放失败"
|
||||
],
|
||||
[
|
||||
9010042,
|
||||
"音频资源重复释放"
|
||||
],
|
||||
[
|
||||
9010043,
|
||||
"音频资源泄漏(未正常释放)"
|
||||
],
|
||||
[
|
||||
9010050,
|
||||
"当前设备不支持该采样率"
|
||||
],
|
||||
[
|
||||
9010051,
|
||||
"声道配置非法或不被支持"
|
||||
],
|
||||
[
|
||||
9010052,
|
||||
"音频格式非法或不被支持"
|
||||
],
|
||||
[
|
||||
9010053,
|
||||
"采集缓冲区大小低于系统最小值"
|
||||
],
|
||||
[
|
||||
9010054,
|
||||
"采集缓冲区大小配置非法"
|
||||
],
|
||||
[
|
||||
9010060,
|
||||
"麦克风被其他应用占用"
|
||||
],
|
||||
[
|
||||
9010061,
|
||||
"音频硬件故障"
|
||||
],
|
||||
[
|
||||
9010062,
|
||||
"系统资源不足,无法创建音频实例"
|
||||
],
|
||||
[
|
||||
9010063,
|
||||
"系统音频服务异常或未启动"
|
||||
],
|
||||
[
|
||||
9010070,
|
||||
"重复启动音频录制"
|
||||
],
|
||||
[
|
||||
9010071,
|
||||
"未在录制状态,无法执行停止/暂停操作"
|
||||
],
|
||||
[
|
||||
9010072,
|
||||
"音频数据缓冲区拷贝失败"
|
||||
],
|
||||
[
|
||||
9010080,
|
||||
"预留错误码"
|
||||
]
|
||||
])
|
||||
@objc(UTSSDKModulesTyRecordingStartAudioRecordFailImpl)
|
||||
@objcMembers
|
||||
public class StartAudioRecordFailImpl : UniError, AudioErrorRes {
|
||||
public init(_ errCode: NSNumber){
|
||||
super.init()
|
||||
self.errSubject = UniErrorSubject
|
||||
self.errCode = errCode
|
||||
self.errMsg = AudioErrors.get(errCode as! AudioErrorCode) ?? ""
|
||||
}
|
||||
}
|
||||
public var isRecording = false
|
||||
public var audioEngine: AVAudioEngine? = nil
|
||||
public var inputNode: AVAudioInputNode? = nil
|
||||
public var reuseInt32Array: Int32Array? = Int32Array(4800)
|
||||
public var recordedPCMData: [UInt8] = []
|
||||
public var targetSampleRate: Double = 16000.0
|
||||
public var channels: NSNumber = 1
|
||||
public var recordStartTime: NSNumber? = nil
|
||||
public func preRequestRecordPermission(_ callback: ((_ granted: Bool) -> Void)?) {
|
||||
var audioSession = AVAudioSession.sharedInstance()
|
||||
do {
|
||||
try audioSession.setCategory(AVAudioSession.Category.record, mode: AVAudioSession.Mode.default)
|
||||
try audioSession.setActive(true)
|
||||
audioSession.requestRecordPermission({
|
||||
(granted: Bool) -> Void in
|
||||
console.log("预申请麦克风权限结果:", granted)
|
||||
do {
|
||||
try audioSession.setActive(false, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
|
||||
}
|
||||
catch let e {
|
||||
var e = UTSError(e)
|
||||
console.warn("预申请后停用音频会话失败:", e)
|
||||
}
|
||||
callback?(granted)
|
||||
})
|
||||
}
|
||||
catch let error {
|
||||
var error = UTSError(error)
|
||||
console.error("预申请麦克风权限失败:", error)
|
||||
callback?(false)
|
||||
}
|
||||
}
|
||||
public func startAudioRecord(_ options: StartAudioRecordOptions) -> Void {
|
||||
console.log("避免重复启动", isRecording)
|
||||
if (isRecording) {
|
||||
return
|
||||
}
|
||||
do {
|
||||
recordedPCMData = Array<UInt8>()
|
||||
var audioSession = AVAudioSession.sharedInstance()
|
||||
try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.spokenAudio, options: AVAudioSession.CategoryOptions.duckOthers)
|
||||
try audioSession.setActive(true)
|
||||
isRecording = true
|
||||
audioSession.requestRecordPermission({
|
||||
(granted: Bool) -> Void in
|
||||
if (!granted) {
|
||||
do {
|
||||
try audioSession.setActive(false, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
|
||||
} catch let e {
|
||||
var e = UTSError(e)
|
||||
console.warn("停用音频会话失败:", e)
|
||||
}
|
||||
isRecording = false
|
||||
var err = StartAudioRecordFailImpl(9010002)
|
||||
options.onError?(err)
|
||||
} else {
|
||||
do {
|
||||
audioEngine = AVAudioEngine.init()
|
||||
if (audioEngine == nil) {
|
||||
throw UTSError("音频输入节点为空,无法获取音频格式")
|
||||
}
|
||||
inputNode = audioEngine!.inputNode
|
||||
if (inputNode == nil) {
|
||||
throw UTSError("音频输入节点为空,无法获取音频格式")
|
||||
}
|
||||
var unwrappedInputNode = inputNode!
|
||||
var inputFormat = unwrappedInputNode.inputFormat(forBus: 0)
|
||||
console.log("硬件原生采样率:", inputFormat.sampleRate)
|
||||
console.log("硬件原生声道数:", inputFormat.channelCount)
|
||||
console.log("硬件原生格式:", inputFormat.commonFormat)
|
||||
recordStartTime = Date.init().getTime()
|
||||
var recordId = """
|
||||
\(Date.init().getTime())_\(Math.floor(Math.random() * 10000))
|
||||
"""
|
||||
options.onStart?(AudioStartRes(UTSJSONObject([
|
||||
"recordId": recordId,
|
||||
"sampleRate": inputFormat.sampleRate,
|
||||
"startTime": recordStartTime
|
||||
])))
|
||||
var targetFormat = AVAudioFormat.init(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: targetSampleRate, channels: inputFormat.channelCount, interleaved: false)
|
||||
unwrappedInputNode.installTap(onBus: 0, bufferSize: 2048, format: inputFormat, block: {
|
||||
(buffer: AVAudioPCMBuffer, time: Any) -> Void in
|
||||
var frameLen = buffer.frameLength as! NSNumber
|
||||
var inputFrameLen = frameLen as! Double
|
||||
var sampleRateRatio = targetSampleRate / inputFormat.sampleRate
|
||||
var outputFrameCounta = inputFrameLen * sampleRateRatio
|
||||
var outputFrameCount = AVAudioFrameCount(outputFrameCounta)
|
||||
var outputBufferOpt = AVAudioPCMBuffer.init(pcmFormat: targetFormat!, frameCapacity: outputFrameCount)
|
||||
outputBufferOpt!.frameLength = outputFrameCount
|
||||
var inputBlock: AVAudioConverterInputBlock = {
|
||||
(_ inNumPackets, _ outStatus) -> AVAudioBuffer in
|
||||
outStatus.pointee = AVAudioConverterInputStatus.haveData
|
||||
return buffer
|
||||
}
|
||||
var convertError: NSError? = nil
|
||||
var converter = AVAudioConverter.init(from: inputFormat, to: targetFormat!)
|
||||
var convertStatus = converter!.convert(to: outputBufferOpt!, error: &convertError, withInputFrom: inputBlock)
|
||||
var int16Ptr = outputBufferOpt!.int16ChannelData
|
||||
var dataPtr = int16Ptr![0]
|
||||
var _recordedPCMData: [UInt8] = []
|
||||
do {
|
||||
var i: Int = 0
|
||||
while(i < outputFrameCount){
|
||||
var int16Value = dataPtr[i]
|
||||
var byte1 = UInt8(int16Value & 0xFF)
|
||||
var byte2 = UInt8((int16Value >> 8) & 0xFF)
|
||||
_recordedPCMData.push(byte1)
|
||||
_recordedPCMData.push(byte2)
|
||||
i++
|
||||
}
|
||||
}
|
||||
options.onFrame?(_recordedPCMData)
|
||||
})
|
||||
try audioEngine!.start()
|
||||
if (audioEngine!.isRunning) {
|
||||
console.log("引擎确实处于运行状态")
|
||||
isRecording = true
|
||||
} else {
|
||||
console.error("调用 start() 但引擎未运行")
|
||||
}
|
||||
isRecording = true
|
||||
}
|
||||
catch let error {
|
||||
var error = UTSError(error)
|
||||
isRecording = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
catch let error {
|
||||
var error = UTSError(error)
|
||||
console.log("报错了", error)
|
||||
var err = StartAudioRecordFailImpl(9010002)
|
||||
options.onError?(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
public func stopAudioRecord() -> Void {
|
||||
console.log("停止音频采集")
|
||||
if (audioEngine == nil) {
|
||||
isRecording = false
|
||||
return
|
||||
}
|
||||
var _audioEngine = audioEngine!
|
||||
if (_audioEngine.isRunning) {
|
||||
_audioEngine.stop()
|
||||
}
|
||||
if (inputNode == nil) {
|
||||
isRecording = false
|
||||
return
|
||||
}
|
||||
var _inputNode = inputNode!
|
||||
_inputNode.removeTap(onBus: 0)
|
||||
console.log("移除音频回调")
|
||||
var audioSession = AVAudioSession.sharedInstance()
|
||||
do {
|
||||
try audioSession.setActive(false)
|
||||
}
|
||||
catch let e {
|
||||
var e = UTSError(e)
|
||||
console.log(e)
|
||||
}
|
||||
inputNode = nil
|
||||
audioEngine = nil
|
||||
isRecording = false
|
||||
}
|
||||
@objc(UTSSDKModulesTyRecordingStartAudioRecordOptionsJSONObject)
|
||||
@objcMembers
|
||||
public class StartAudioRecordOptionsJSONObject : NSObject {
|
||||
public var autoStopDuration: NSNumber?
|
||||
public var onFrame: UTSCallback?
|
||||
public var onVolume: UTSCallback?
|
||||
public var onStart: UTSCallback?
|
||||
public var onError: UTSCallback?
|
||||
public var onStop: UTSCallback?
|
||||
public var onFile: UTSCallback?
|
||||
}
|
||||
public func preRequestRecordPermissionByJs(_ callback: UTSCallback?) {
|
||||
return preRequestRecordPermission({
|
||||
(granted: Bool) -> Void in
|
||||
callback?(granted)
|
||||
})
|
||||
}
|
||||
public func startAudioRecordByJs(_ options: StartAudioRecordOptionsJSONObject) -> Void {
|
||||
return startAudioRecord(StartAudioRecordOptions(UTSJSONObject([
|
||||
"autoStopDuration": options.autoStopDuration,
|
||||
"onFrame": {
|
||||
(frameData: Any) -> Void in
|
||||
options.onFrame?(frameData)
|
||||
},
|
||||
"onVolume": {
|
||||
(volume: NSNumber) -> Void in
|
||||
options.onVolume?(volume)
|
||||
},
|
||||
"onStart": {
|
||||
(res: AudioStartRes) -> Void in
|
||||
options.onStart?(res)
|
||||
},
|
||||
"onError": {
|
||||
(res: AudioErrorRes) -> Void in
|
||||
options.onError?(res)
|
||||
},
|
||||
"onStop": {
|
||||
() -> Void in
|
||||
options.onStop?()
|
||||
},
|
||||
"onFile": {
|
||||
(res: AudioFileRes) -> Void in
|
||||
options.onFile?(res)
|
||||
}
|
||||
])))
|
||||
}
|
||||
public func stopAudioRecordByJs() -> Void {
|
||||
return stopAudioRecord()
|
||||
}
|
||||
@objc(UTSSDKModulesTyRecordingIndexSwift)
|
||||
@objcMembers
|
||||
public class UTSSDKModulesTyRecordingIndexSwift : NSObject {
|
||||
public static func s_preRequestRecordPermissionByJs(_ callback: UTSCallback?) {
|
||||
return preRequestRecordPermissionByJs(callback)
|
||||
}
|
||||
public static func s_startAudioRecordByJs(_ options: StartAudioRecordOptionsJSONObject) -> Void {
|
||||
return startAudioRecordByJs(options)
|
||||
}
|
||||
public static func s_stopAudioRecordByJs() -> Void {
|
||||
return stopAudioRecordByJs()
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "Sunmi-Print-Inner",
|
||||
"id": "Sunmi-Print-Inner",
|
||||
"version": "1.7",
|
||||
"description": "商米打印服务uniapp插件",
|
||||
"_dp_type":"nativeplugin",
|
||||
"_dp_nativeplugin":{
|
||||
"android": {
|
||||
"plugins": [
|
||||
{
|
||||
"type": "module",
|
||||
"name": "Sunmi-Print-Inner",
|
||||
"class": "com.sunmi.uniprint.SunmiPrintI"
|
||||
}
|
||||
],
|
||||
"hooksClass": "com.sunmi.uniprint.SunmiPrinterProxy",
|
||||
"integrateType": "aar",
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"minSdkVersion": "22"
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
@file:Suppress("UNCHECKED_CAST", "USELESS_CAST", "INAPPLICABLE_JVM_NAME", "UNUSED_ANONYMOUS_PARAMETER", "NAME_SHADOWING", "UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
package uts.sdk.modules.tyRecording
|
||||
import android.media.AudioFormat
|
||||
import android.media.AudioRecord
|
||||
import android.media.MediaRecorder
|
||||
import io.dcloud.uniapp.*
|
||||
import io.dcloud.uniapp.extapi.*
|
||||
import io.dcloud.uts.*
|
||||
import io.dcloud.uts.Map
|
||||
import io.dcloud.uts.Set
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
open class AudioStartRes (
|
||||
@JsonNotNull
|
||||
open var recordId: String,
|
||||
open var sampleRate: Number? = null,
|
||||
@JsonNotNull
|
||||
open var startTime: Number,
|
||||
) : UTSObject()
|
||||
open class AudioFileRes (
|
||||
@JsonNotNull
|
||||
open var filePath: String,
|
||||
@JsonNotNull
|
||||
open var fileSize: Number,
|
||||
@JsonNotNull
|
||||
open var duration: Number,
|
||||
@JsonNotNull
|
||||
open var format: String,
|
||||
@JsonNotNull
|
||||
open var endTime: Number,
|
||||
) : UTSObject()
|
||||
typealias AudioErrorCode = Number
|
||||
interface AudioErrorRes : IUniError {
|
||||
override var errCode: Number
|
||||
}
|
||||
open class StartAudioRecordOptions (
|
||||
open var autoStopDuration: Number? = null,
|
||||
open var onFrame: ((frameData: Any) -> Unit)? = null,
|
||||
open var onVolume: ((volume: Number) -> Unit)? = null,
|
||||
open var onStart: ((res: AudioStartRes) -> Unit)? = null,
|
||||
open var onError: ((res: AudioErrorRes) -> Unit)? = null,
|
||||
open var onStop: (() -> Unit)? = null,
|
||||
open var onFile: ((res: AudioFileRes) -> Unit)? = null,
|
||||
) : UTSObject()
|
||||
val UniErrorSubject = "ty-recording"
|
||||
val AudioErrors = Map<AudioErrorCode, String>(utsArrayOf(
|
||||
utsArrayOf(
|
||||
9010001,
|
||||
"麦克风权限未授予,请前往设置开启1"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010002,
|
||||
"麦克风权限被永久拒绝,请前往设置开启"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010003,
|
||||
"获取麦克风权限状态失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010010,
|
||||
"音频录制实例初始化失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010011,
|
||||
"音频录制实例未初始化,无法执行操作"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010012,
|
||||
"系统资源不足,无法创建音频录制实例"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010020,
|
||||
"音频采集读取数据失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010021,
|
||||
"音频采集被中断(麦克风被其他应用抢占)"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010022,
|
||||
"音频采集缓冲区无有效数据"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010023,
|
||||
"启动音频采集失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010030,
|
||||
"采集线程创建失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010031,
|
||||
"采集线程启动失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010032,
|
||||
"采集线程中断失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010033,
|
||||
"采集线程运行崩溃"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010040,
|
||||
"停止音频采集失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010041,
|
||||
"音频录制资源释放失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010042,
|
||||
"音频资源重复释放"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010043,
|
||||
"音频资源泄漏(未正常释放)"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010050,
|
||||
"当前设备不支持该采样率"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010051,
|
||||
"声道配置非法或不被支持"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010052,
|
||||
"音频格式非法或不被支持"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010053,
|
||||
"采集缓冲区大小低于系统最小值"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010054,
|
||||
"采集缓冲区大小配置非法"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010060,
|
||||
"麦克风被其他应用占用"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010061,
|
||||
"音频硬件故障"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010062,
|
||||
"系统资源不足,无法创建音频实例"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010063,
|
||||
"系统音频服务异常或未启动"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010070,
|
||||
"重复启动音频录制"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010071,
|
||||
"未在录制状态,无法执行停止/暂停操作"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010072,
|
||||
"音频数据缓冲区拷贝失败"
|
||||
),
|
||||
utsArrayOf(
|
||||
9010080,
|
||||
"预留错误码"
|
||||
)
|
||||
))
|
||||
open class StartAudioRecordFailImpl : UniError, AudioErrorRes {
|
||||
constructor(errCode: Number) : super() {
|
||||
this.errSubject = UniErrorSubject
|
||||
this.errCode = errCode
|
||||
this.errMsg = AudioErrors.get(errCode as AudioErrorCode) ?: ""
|
||||
}
|
||||
}
|
||||
val isRecording = AtomicBoolean(false)
|
||||
val sampleRate: Int = 16000
|
||||
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
||||
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
||||
val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
|
||||
val bufferSize = minBufferSize * 2
|
||||
var audioRecord: AudioRecord? = null
|
||||
var recordThread: Thread? = null
|
||||
var recordStartTime: Number? = null
|
||||
fun startAudioRecord(options: StartAudioRecordOptions): Unit {
|
||||
if (isRecording.get()) {
|
||||
return
|
||||
}
|
||||
val _audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, bufferSize)
|
||||
if (_audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||
val err = StartAudioRecordFailImpl(9010001)
|
||||
options.onError?.invoke(err)
|
||||
return
|
||||
}
|
||||
audioRecord = _audioRecord
|
||||
isRecording.set(true)
|
||||
val _recordThread = Thread(fun(){
|
||||
_audioRecord.startRecording()
|
||||
val recordId = "" + Date().getTime() + "_" + Math.floor(Math.random() * 10000)
|
||||
recordStartTime = Date().getTime()
|
||||
options.onStart?.invoke(AudioStartRes(startTime = recordStartTime as Number, sampleRate = sampleRate as Number, recordId = recordId))
|
||||
console.log("AudioRecord已启动采集")
|
||||
val buffer = ByteArray(bufferSize)
|
||||
while(isRecording.get()){
|
||||
val readSize = _audioRecord.read(buffer, 0, bufferSize)
|
||||
if (readSize > 0) {
|
||||
val data = ByteArray(readSize)
|
||||
java.lang.System.arraycopy(buffer, 0, data, 0, readSize)
|
||||
options.onFrame?.invoke(data)
|
||||
}
|
||||
}
|
||||
if (_audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
||||
console.log("停止采集xxxxxxxxx")
|
||||
_audioRecord.stop()
|
||||
options.onStop?.invoke()
|
||||
}
|
||||
}
|
||||
)
|
||||
_recordThread.start()
|
||||
recordThread = _recordThread
|
||||
}
|
||||
fun stopAudioRecord(): Unit {
|
||||
console.log("停止麦克风采集", isRecording.get())
|
||||
if (!isRecording.get()) {
|
||||
return
|
||||
}
|
||||
isRecording.set(false)
|
||||
val _audioRecord = audioRecord
|
||||
if (_audioRecord != null) {
|
||||
try {
|
||||
if (_audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
||||
_audioRecord.stop()
|
||||
}
|
||||
_audioRecord.release()
|
||||
}
|
||||
catch (e: Throwable) {}
|
||||
}
|
||||
audioRecord = null
|
||||
console.log("等待线程结束")
|
||||
val _recordThread = recordThread
|
||||
if (_recordThread != null) {
|
||||
try {
|
||||
_recordThread.join(1000)
|
||||
console.log("后台线程已停止")
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
console.warn("停止后台线程时出现异常:", e)
|
||||
}
|
||||
finally{
|
||||
recordThread = null
|
||||
}
|
||||
}
|
||||
}
|
||||
fun preRequestRecordPermission(): Unit {}
|
||||
open class StartAudioRecordOptionsJSONObject : UTSJSONObject() {
|
||||
open var autoStopDuration: Number? = null
|
||||
open var onFrame: UTSCallback? = null
|
||||
open var onVolume: UTSCallback? = null
|
||||
open var onStart: UTSCallback? = null
|
||||
open var onError: UTSCallback? = null
|
||||
open var onStop: UTSCallback? = null
|
||||
open var onFile: UTSCallback? = null
|
||||
}
|
||||
fun startAudioRecordByJs(options: StartAudioRecordOptionsJSONObject): Unit {
|
||||
return startAudioRecord(StartAudioRecordOptions(autoStopDuration = options.autoStopDuration, onFrame = fun(frameData: Any): Unit {
|
||||
options.onFrame?.invoke(frameData)
|
||||
}
|
||||
, onVolume = fun(volume: Number): Unit {
|
||||
options.onVolume?.invoke(volume)
|
||||
}
|
||||
, onStart = fun(res: AudioStartRes): Unit {
|
||||
options.onStart?.invoke(res)
|
||||
}
|
||||
, onError = fun(res: AudioErrorRes): Unit {
|
||||
options.onError?.invoke(res)
|
||||
}
|
||||
, onStop = fun(): Unit {
|
||||
options.onStop?.invoke()
|
||||
}
|
||||
, onFile = fun(res: AudioFileRes): Unit {
|
||||
options.onFile?.invoke(res)
|
||||
}
|
||||
))
|
||||
}
|
||||
fun stopAudioRecordByJs(): Unit {
|
||||
return stopAudioRecord()
|
||||
}
|
||||
fun preRequestRecordPermissionByJs(): Unit {
|
||||
return preRequestRecordPermission()
|
||||
}
|
||||
+4
-8
@@ -7,20 +7,17 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dev:h5": "uni",
|
||||
"dev:rd": "uni -p h5 --mode development.rd",
|
||||
"dev:yh": "uni -p h5 --mode development.yh",
|
||||
"dev:ty": "uni -p h5 --mode development.ty",
|
||||
"dev:h5:sit": "uni -p h5 --mode sit",
|
||||
"dev:h5:uat": "uni -p h5 --mode uat",
|
||||
"dev:h5:production": "uni -p h5 --mode production",
|
||||
"build:app-plus:dev": "uni build -p app-plus --mode development",
|
||||
"build:app-plus:develop": "uni build -p app-plus --mode development",
|
||||
"build:app-plus:sit": "uni build -p app-plus --mode sit",
|
||||
"build:app-plus:uat": "uni build -p app-plus --mode uat",
|
||||
"build:app-plus:prod": "uni build -p app-plus",
|
||||
"build:h5:dev": "uni build -p h5 --mode development_h5",
|
||||
"build:h5": "uni build",
|
||||
"b": "uni build -p app-plus",
|
||||
"build:h5:dev": "uni build -p h5 --mode development_h5",
|
||||
"build:h5": "uni build"
|
||||
"i": "npm install --registry=https://registry.npmmirror.com"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-4060620250520001",
|
||||
@@ -43,11 +40,10 @@
|
||||
"dompurify": "^3.3.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"pinia": "^2.3.1",
|
||||
"pnpm": "^10.25.0",
|
||||
"terser": "^5.42.0",
|
||||
"text-encoding": "^0.7.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.11",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+2
-3
@@ -2,12 +2,11 @@ import {
|
||||
defineConfig
|
||||
} from 'vite'
|
||||
import uni from '@dcloudio/vite-plugin-uni'
|
||||
import path from 'path'
|
||||
export default defineConfig({
|
||||
plugins: [uni()],
|
||||
// 新增服务器配置,指定启动端口
|
||||
server: {
|
||||
port: 5174,
|
||||
port: 5173,
|
||||
strictPort: true, // 可选:端口被占用时直接报错(避免自动切换端口)
|
||||
open: false // 可选:启动后自动打开浏览器(按需开启)
|
||||
},
|
||||
@@ -16,4 +15,4 @@ export default defineConfig({
|
||||
outDir: 'dist',
|
||||
assetsDir: 'assets' // 和打包后的目录一致
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user