新增语音输入组件,调整H5使用的方法,保证能唤起麦克风
This commit is contained in:
parent
9e2daf8b2e
commit
3b70cafff6
20
env/.env.prod
vendored
20
env/.env.prod
vendored
@ -5,17 +5,17 @@ VITE_SHOW_CONSOLE = false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = false
|
||||
|
||||
# # baseUrl
|
||||
# VITE_BASEURL = 'https://chat-out.szjixun.cn' #体制外
|
||||
# #VITE_SOCKET_API
|
||||
# VITE_SOCKET_API = 'wss://chat-out.szjixun.cn' #体制外
|
||||
# # EPRAPI baseUrl
|
||||
# VITE_EPR_BASEURL = 'https://erpapi-out.szjixun.cn' #体制外
|
||||
|
||||
# baseUrl
|
||||
VITE_BASEURL = 'https://chat.szjixun.cn' #体制内
|
||||
VITE_BASEURL = 'https://chat-out.szjixun.cn' #体制外
|
||||
#VITE_SOCKET_API
|
||||
VITE_SOCKET_API = 'wss://chat.szjixun.cn' #体制内
|
||||
VITE_SOCKET_API = 'wss://chat-out.szjixun.cn' #体制外
|
||||
# EPRAPI baseUrl
|
||||
VITE_EPR_BASEURL = 'https://erpapi.fontree.cn' #体制内
|
||||
VITE_EPR_BASEURL = 'https://erpapi-out.szjixun.cn' #体制外
|
||||
|
||||
# # baseUrl
|
||||
# VITE_BASEURL = 'https://chat.szjixun.cn' #体制内
|
||||
# #VITE_SOCKET_API
|
||||
# VITE_SOCKET_API = 'wss://chat.szjixun.cn' #体制内
|
||||
# # EPRAPI baseUrl
|
||||
# VITE_EPR_BASEURL = 'https://erpapi.fontree.cn' #体制内
|
||||
|
||||
|
@ -31,16 +31,17 @@
|
||||
"@uni-helper/axios-adapter": "^1.5.2",
|
||||
"@uni-helper/localforage-adapter": "^1.0.2",
|
||||
"@uni-helper/uni-use": "^0.19.12",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"@vueup/vue-quill": "^1.2.0",
|
||||
"quill": "^1.3.7",
|
||||
"quill-mention": "^4.1.0",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"axios": "^1.7.2",
|
||||
"dayjs": "^1.11.12",
|
||||
"less": "^4.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nzh": "^1.0.13",
|
||||
"pinia-plugin-persistedstate": "^4.1.3",
|
||||
"quill": "^1.3.7",
|
||||
"quill-mention": "^4.1.0",
|
||||
"recorder-core": "^1.3.25011100",
|
||||
"vconsole": "^3.15.1",
|
||||
"vue": "^3.3.8",
|
||||
"vue-i18n": "11.0.0-rc.1"
|
||||
|
10303
pnpm-lock.yaml
10303
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -263,7 +263,7 @@
|
||||
@editorChange="onEditorChange"
|
||||
style="width: 100%; flex: 1; height: 100%;"
|
||||
@click="onEditorClick"
|
||||
v-if="state.canUseQuillEditor"
|
||||
v-if="state.canUseQuillEditor && !state.isUseSpeech"
|
||||
/>
|
||||
<tm-input
|
||||
type="textarea"
|
||||
@ -272,11 +272,23 @@
|
||||
color="#F9F9F9"
|
||||
:inputPadding="[12]"
|
||||
placeholder=""
|
||||
v-if="!state.canUseQuillEditor"
|
||||
v-if="!state.canUseQuillEditor && !state.isUseSpeech"
|
||||
@update:modelValue="onTextAreaChange"
|
||||
v-model="state.textAreaValue"
|
||||
@input="onTextAreaInput"
|
||||
></tm-input>
|
||||
<allSpeech
|
||||
@startRecord="startRecord"
|
||||
@endRecord="endRecord"
|
||||
@cancelRecord="cancelRecord"
|
||||
popupTitle=""
|
||||
popupDefaultTips=""
|
||||
:btnDefaultText="$t('hold_to') + ' ' + $t('speak')"
|
||||
lineStartColor="#fff"
|
||||
lineEndColor="#fff"
|
||||
:chatInputHeight="chatInputHeight"
|
||||
v-if="state.isUseSpeech"
|
||||
></allSpeech>
|
||||
<div class="quote-area" v-if="state?.quoteInfo">
|
||||
<span
|
||||
v-if="state?.quoteInfo?.msg_type === 1"
|
||||
@ -333,6 +345,7 @@
|
||||
label="发送"
|
||||
:loading="state.isLoading"
|
||||
:width="120"
|
||||
v-if="!state.isUseSpeech"
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -523,6 +536,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import allSpeech from '@/uni_modules/all-speech/components/all-speech/all-speech.vue'
|
||||
import selectMemberByAlphabet from '../chatSettings/components/selectMemberByAlphabet.vue'
|
||||
import {
|
||||
ref,
|
||||
@ -652,6 +666,7 @@ const state = ref({
|
||||
lastSelection: 0,
|
||||
canUseQuillEditor: true, //是否可以使用quill编辑器,如果版本不支持,则使用普通输入框
|
||||
textAreaValue: '', //普通输入框的值
|
||||
isUseSpeech: true, //是否使用语音输入
|
||||
})
|
||||
|
||||
uniOnload(async (options) => {
|
||||
|
BIN
src/static/image/record/chat-voice-animation-bg-blue.png
Normal file
BIN
src/static/image/record/chat-voice-animation-bg-blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
src/static/image/record/chat-voice-animation-bg-red.png
Normal file
BIN
src/static/image/record/chat-voice-animation-bg-red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
src/static/image/record/chat-voice-cancel-blue.png
Normal file
BIN
src/static/image/record/chat-voice-cancel-blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
src/static/image/record/chat-voice-cancel-grey.png
Normal file
BIN
src/static/image/record/chat-voice-cancel-grey.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
src/static/image/record/chat-voice-icon-blue.png
Normal file
BIN
src/static/image/record/chat-voice-icon-blue.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
src/static/image/record/chat-voice-icon-white.png
Normal file
BIN
src/static/image/record/chat-voice-icon-white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
38
src/uni_modules/Recorder-UniCore/app-uni-support.js
Normal file
38
src/uni_modules/Recorder-UniCore/app-uni-support.js
Normal file
File diff suppressed because one or more lines are too long
19
src/uni_modules/Recorder-UniCore/changelog.md
Normal file
19
src/uni_modules/Recorder-UniCore/changelog.md
Normal file
@ -0,0 +1,19 @@
|
||||
## 1.0.250331(2025-03-31)
|
||||
增加RecordApp.UniNativeUtsPlugin_OnJsCall接口,App端搭配原生插件使用时,可绑定接收配套原生录音插件事件:原生插件新增PcmPlayer播放器,支持流式播放、完整播放,App端边录音边播放更流畅
|
||||
## 1.0.250111(2025-01-11)
|
||||
修复vue3 Fragments(multi-root 多个根节点)的兼容性问题;修复uniapp Android自带的XXPermissions库在后台无法请求权限的问题(仅限搭配原生录音插件可用)
|
||||
## 1.0.241020(2024-10-20)
|
||||
适配HBuilder4.28 vue3 setup编译环境下$root.$scope无法读取的bug,HBuilder4.29已修复此编译bug,但似乎还是有不能使用的问题。如果setup内不能使用,可尝试新建个vue组件,然后使用选项式api来调用录音功能,页面的setup内使用此vue组件
|
||||
## 1.0.240910(2024-09-10)
|
||||
- 新增RecordApp.UniMainCallBack_Register接口,允许App renderjs层多次回调数据给逻辑层
|
||||
- iOS App请求权限时,会预先检查NSMicrophoneUsageDescription是否声明,避免无声明时调用录音会崩溃
|
||||
- 新增appNativePlugin_sampleRate原生插件录音选项
|
||||
- Android App已提供后台录音保活功能,启用后App在后台或锁屏后可继续正常录音
|
||||
## 1.0.240625(2024-06-25)
|
||||
调整UniWebViewCallAsync调用失败时返回更详细信息。android_audioSource默认值由1改成0,新增ios_categoryOptions原生插件录音选项
|
||||
## 1.0.240409(2024-04-09)
|
||||
增加功能调用,完善demo项目
|
||||
## 1.0.231208(2023-12-08)
|
||||
完善文档,增加asr语音识别示例
|
||||
## 1.0.231201(2023-12-04)
|
||||
第一次发布
|
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<view>
|
||||
<view style="font-weight: bold;">Recorder-UniCore Vue Component</view>
|
||||
<view style="font-size:14px; color:#f60">无需手动显示本UI组件,只需在script中正常引入 RecordApp + app-uni-support.js 即可实现 H5、iOS Android App、微信小程序 多端录音</view>
|
||||
</view>
|
||||
</template>
|
495
src/uni_modules/Recorder-UniCore/i18n/Template.js
Normal file
495
src/uni_modules/Recorder-UniCore/i18n/Template.js
Normal file
@ -0,0 +1,495 @@
|
||||
/*
|
||||
Recorder ../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/i18n/Template.js
|
||||
https://github.com/xiangyuecn/Recorder
|
||||
|
||||
Usage: Recorder.i18n.lang="Your-Language-Name" or "your-language"
|
||||
|
||||
Desc: This file is a language translation template file. After copying and renaming, translate the text into the corresponding language. 此文件为语言翻译模板文件,复制并改名后,将文本翻译成对应语言即可。
|
||||
|
||||
注意:请勿修改//@@打头的文本行;以下代码结构由/src/package-i18n.js自动生成,只允许在字符串中填写翻译后的文本,请勿改变代码结构;翻译的文本如果需要明确的空值,请填写"=Empty";文本中的变量用{n}表示(n代表第几个变量),所有变量必须都出现至少一次,如果不要某变量用{n!}表示
|
||||
|
||||
Note: Do not modify the text lines starting with //@@; The following code structure is automatically generated by /src/package-i18n.js, only the translated text is allowed to be filled in the string, please do not change the code structure; If the translated text requires an explicit empty value, please fill in "=Empty"; Variables in the text are represented by {n} (n represents the number of variables), all variables must appear at least once, if a variable is not required, it is represented by {n!}
|
||||
*/
|
||||
(function(factory){
|
||||
var browser=typeof window=="object" && !!window.document;
|
||||
var win=browser?window:Object; //非浏览器环境,Recorder挂载在Object下面
|
||||
factory(win.Recorder,browser);
|
||||
}(function(Recorder,isBrowser){
|
||||
"use strict";
|
||||
var i18n=Recorder.i18n;
|
||||
|
||||
//@@User Code-1 Begin 手写代码放这里 Put the handwritten code here @@
|
||||
|
||||
//@@User Code-1 End @@
|
||||
|
||||
//@@Exec i18n.lang="Your-Language-Name";
|
||||
Recorder.CLog('Import Page[Recorder_UniCore] lang="Your-Language-Name"');
|
||||
|
||||
//@@Exec i18n.alias["Your-Language-Name"]="your-language";
|
||||
|
||||
var putSet={lang:"your-language"};
|
||||
|
||||
//@@Exec i18n.data["rtl$your-language"]=false;
|
||||
i18n.data["desc-page-Recorder_UniCore$your-language"]="This file is a language translation template file. After copying and renaming, translate the text into the corresponding language. 此文件为语言翻译模板文件,复制并改名后,将文本翻译成对应语言即可。";
|
||||
//@@Exec i18n.GenerateDisplayEnglish=true;
|
||||
|
||||
|
||||
|
||||
//*************** Begin srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
|
||||
i18n.put(putSet,
|
||||
[ //@@PutList
|
||||
|
||||
//@@zh="微信小程序中需要:{1}"
|
||||
//@@en="WeChat miniProgram requires: {1}"
|
||||
//@@Put0
|
||||
"RXs7:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="Recorder-UniCore目前只支持:H5、APP(Android iOS)、MP-WEIXIN,其他平台环境需要自行编写适配文件实现接入"
|
||||
//@@en="Recorder-UniCore currently only supports: H5, APP (Android iOS), MP-WEIXIN, other platform environments need to write their own adaptation files to achieve access"
|
||||
,"4ATo:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniWebViewActivate 需要传入当前页面或组件的this对象作为参数"
|
||||
//@@en="RecordApp.UniWebViewActivate needs to pass in the this object of the current page or component as a parameter"
|
||||
,"GwCz:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniWebViewActivate 发生不应该出现的错误(可能需要升级插件代码):"
|
||||
//@@en="An error occurred in RecordApp.UniWebViewActivate that should not occur (the plug-in code may need to be upgraded): "
|
||||
,"ipB3:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniWebViewActivate 已切换当前页面或组件的renderjs所在的WebView"
|
||||
//@@en="RecordApp.UniWebViewActivate has switched the WebView where the renderjs of the current page or component is located"
|
||||
,"WpKg:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniRenderjsRegister 发生不应该出现的错误(可能需要升级插件代码):"
|
||||
//@@en="An error occurred in RecordApp.UniRenderjsRegister that should not occur (the plugin code may need to be upgraded): "
|
||||
,"Uc9E:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniRenderjsRegister 重复注册当前页面renderjs模块,一个组件内只允许一个renderjs模块进行注册"
|
||||
//@@en="RecordApp.UniRenderjsRegister repeatedly registers the renderjs module of the current page. Only one renderjs module is allowed to be registered in a component"
|
||||
,"mzKj:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniRenderjsRegister 已注册当前页面renderjs模块"
|
||||
//@@en="RecordApp.UniRenderjsRegister has registered the renderjs module of the current page"
|
||||
,"7kJS:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="严重兼容性问题:无法获取页面或组件this.$root.$scope或.$page"
|
||||
//@@en="Serious compatibility issue: Unable to get page or component this.$root.$scope or .$page"
|
||||
,"KpY6:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需要先调用RecordApp.UniWebViewActivate方法"
|
||||
//@@en="You need to call the RecordApp.UniWebViewActivate method first"
|
||||
,"AGd7:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需先调用RecordApp.RequestPermission方法"
|
||||
//@@en="You need to call the RecordApp.RequestPermission method first"
|
||||
,"7ot0:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需重新调用RecordApp.RequestPermission方法"
|
||||
//@@en="The RecordApp.RequestPermission method needs to be called again"
|
||||
,"VsdN:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="检测到有其他页面或组件调用了RecordApp.UniPageOnShow(WvCid={1}),但未调用过RecordApp.UniWebViewActivate(当前WvCid={2}),部分功能会继续使用之前Activate的WebView和组件,请确保这是符合你的业务逻辑,不是因为忘记了调用UniWebViewActivate"
|
||||
//@@en="It is detected that another page or component has called RecordApp.UniPageOnShow (WvCid={1}), but RecordApp.UniWebViewActivate (current WvCid={2}) has not been called. Some functions will continue to use the previously Activated WebView and components. Please make sure This is in line with your business logic, not because you forgot to call UniWebViewActivate"
|
||||
,"SWsy:"+ //args: {1}-{2}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="{1}未正确查询到节点,将使用传入的当前页面或组件this的$el.parentNode作为组件根节点。如果template下存在多个根节点(vue3 multi-root),尽量在最外面再套一层view来避免兼容性问题"
|
||||
//@@en="{1} does not query the node correctly, and will use the current page or component this's $el.parentNode as the component root node. If there are multiple root nodes under the template (vue3 multi-root), try to add another layer of view on the outermost to avoid compatibility issues"
|
||||
,"dX7B:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="{1}需在renderjs中调用并且传入当前模块的this"
|
||||
//@@en="{1} needs to be called in renderjs and pass in this of the current module"
|
||||
,"dX5B:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="{1}需要传入当前页面或组件的this对象作为参数"
|
||||
//@@en="{1} needs to pass in the this object of the current page or component as a parameter"
|
||||
,"dX6B:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前不是App逻辑层"
|
||||
//@@en="Currently it is not the App logic layer"
|
||||
,"TfJX:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
|
||||
//@@en="RecordApp.UniWebViewActivate has not been called yet"
|
||||
,"peIm:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未找到此页面renderjs所在的WebView"
|
||||
//@@en="The WebView where renderjs for this page is not found"
|
||||
,"qDo1:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh=",不可以调用RecordApp.UniWebViewEval"
|
||||
//@@en=", RecordApp.UniWebViewEval cannot be called"
|
||||
,"igw2:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前不是App逻辑层"
|
||||
//@@en="Currently it is not the App logic layer"
|
||||
,"lU1W:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
|
||||
//@@en="RecordApp.UniWebViewActivate has not been called yet"
|
||||
,"mSbR:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未找到此页面renderjs所在的WebView Cid"
|
||||
//@@en="The WebView Cid where renderjs for this page is not found"
|
||||
,"6Iql:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh=",不可以调用RecordApp.UniWebViewVueCall"
|
||||
//@@en=", RecordApp.UniWebViewVueCall cannot be called"
|
||||
,"TtoS:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="renderjs中未import导入RecordApp"
|
||||
//@@en="RecordApp is not imported in renderjs"
|
||||
,"U1Be:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister"
|
||||
//@@en="RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs"
|
||||
,"Bcgi:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="没有找到组件的renderjs模块"
|
||||
//@@en="The renderjs module for the component was not found"
|
||||
,"URyD:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="{1}连接renderjs超时"
|
||||
//@@en="{1} connection renderjs timeout"
|
||||
,"KQhJ:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="{1}处理超时"
|
||||
//@@en="{1} processing timeout"
|
||||
,"RDcZ:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需要在页面中提供一个renderjs,在里面import导入RecordApp、录音格式编码器、可视化插件等"
|
||||
//@@en="You need to provide a renderjs in the page, and import RecordApp, recording format encoder, visualization plug-in, etc."
|
||||
,"TSmQ:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需在renderjs中import {1}"
|
||||
//@@en="Need to import {1} in renderjs"
|
||||
,"AN0e:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="不应该出现的MainReceiveBind重复绑定"
|
||||
//@@en="MainReceiveBind duplicate binding that should not occur"
|
||||
,"vEgr:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="从renderjs发回数据但UniMainCallBack回调不存在:"
|
||||
//@@en="Sending data back from renderjs but UniMainCallBack callback does not exist: "
|
||||
,"kZx6:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="[MainReceive]从renderjs发回未知数据:"
|
||||
//@@en="[MainReceive] Unknown data sent back from renderjs: "
|
||||
,"ZHwv:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="只允许在renderjs中调用RecordApp.UniWebViewSendBigBytesToMain"
|
||||
//@@en="Only allowed to call RecordApp.UniWebViewSendBigBytesToMain in renderjs"
|
||||
,"MujG:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister才能调用RecordApp.UniWebViewSendBigBytesToMain"
|
||||
//@@en="RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs to call RecordApp.UniWebViewSendBigBytesToMain"
|
||||
,"kE91:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="无效的BigBytes回传数据"
|
||||
//@@en="Invalid BigBytes return data"
|
||||
,"CjMb:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="保存文件{1}失败:"
|
||||
//@@en="Failed to save file {1}: "
|
||||
,"UqfI:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前环境未支持保存本地文件"
|
||||
//@@en="The current environment does not support saving local files"
|
||||
,"kxOd:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh=" | RecordApp的uni-app支持文档和示例:{1} "
|
||||
//@@en=" | RecordApp’s uni-app support documentation and examples: {1}"
|
||||
,"1f2V:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前录音由原生录音插件提供支持"
|
||||
//@@en="Current recording is powered by native recording plug-in"
|
||||
,"XSYY:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前录音由uts插件提供支持"
|
||||
//@@en="Current recording is powered by uts plugin"
|
||||
,"nnM6:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前已配置RecordApp.UniWithoutAppRenderjs,必须提供原生录音插件或uts插件才能录音,请参考RecordApp.UniNativeUtsPlugin配置"
|
||||
//@@en="RecordApp.UniWithoutAppRenderjs is currently configured. A native recording plug-in or uts plug-in must be provided to record. Please refer to the RecordApp.UniNativeUtsPlugin configuration"
|
||||
,"fqhr:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前RecordApp运行在逻辑层中(性能会略低一些,可视化等插件不可用)"
|
||||
//@@en="Currently RecordApp runs in the logical layer (performance will be slightly lower, and plug-ins such as visualization are not available) "
|
||||
,"xYRb:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未找到当前页面renderjs所在的WebView"
|
||||
//@@en="The WebView where renderjs of the current page is located is not found"
|
||||
,"S3eF:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前RecordApp运行在renderjs所在的WebView中(逻辑层中只能做有限的实时处理,可视化等插件均需要在renderjs中进行调用)"
|
||||
//@@en="The current RecordApp runs in the WebView where renderjs is located (only limited real-time processing can be done in the logic layer, and visualization and other plug-ins need to be called in renderjs) "
|
||||
,"0hyi:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh=",请检查此页面代码中是否编写了lang=renderjs的module,并且调用了RecordApp.UniRenderjsRegister;如果确实没有renderjs,比如nvue页面,请设置RecordApp.UniWithoutAppRenderjs=true并且搭配配套的原生插件在逻辑层中直接录音"
|
||||
//@@en=", please check whether the module with lang=renderjs is written in the code of this page and RecordApp.UniRenderjsRegister is called; if there is indeed no renderjs, such as nvue page, please set RecordApp.UniWithoutAppRenderjs=true and use the matching native plug-in to record directly in the logic layer"
|
||||
,"e6Mo:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="【在App内使用{1}的授权许可】"
|
||||
//@@en="[License for use of {1} within the App] "
|
||||
,"FabE:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="已购买原生录音插件,获得授权许可"
|
||||
//@@en="Purchased the native recording plug-in and obtained the license"
|
||||
,"w37G:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="已购买uts插件,获得授权许可"
|
||||
//@@en="Purchased uts plug-in and obtained license"
|
||||
,"e71S:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="UniAppUseLicense填写无效,如果已获取到了商用授权,请填写:{1},否则请使用空字符串"
|
||||
//@@en="UniAppUseLicense is invalid. If you have obtained a commercial license, please fill in: {1}, otherwise please use an empty string"
|
||||
,"aPoj:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未找到Canvas:{1},请确保此DOM已挂载(可尝试用$nextTick等待DOM更新)"
|
||||
//@@en="Canvas not found: {1}, please make sure this DOM is mounted (try $nextTick to wait for DOM update) "
|
||||
,"k7im:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="RecordApp.UniFindCanvas未适配当前环境"
|
||||
//@@en="RecordApp.UniFindCanvas does not adapt to the current environment"
|
||||
,"yI24:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未配置RecordApp.UniNativeUtsPlugin原生录音插件"
|
||||
//@@en="RecordApp.UniNativeUtsPlugin native recording plug-in is not configured"
|
||||
,"H753:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="renderjs中不支持设置RecordApp.UniNativeUtsPlugin"
|
||||
//@@en="Setting RecordApp.UniNativeUtsPlugin is not supported in renderjs"
|
||||
,"l6sY:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="当前App未打包进双端原生插件[{1}],尝试加载单端[{2}]"
|
||||
//@@en="The current App is not packaged into the dual-end native plug-in [{1}], try to load the single-end [{2}]"
|
||||
,"kSjQ:"+ //args: {1}-{2}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="已加载原生录音插件[{1}]"
|
||||
//@@en="Native recording plugin loaded [{1}]"
|
||||
,"Xh1W:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="配置了RecordApp.UniNativeUtsPlugin,但当前App未打包进原生录音插件[{1}]"
|
||||
//@@en="RecordApp.UniNativeUtsPlugin is configured, but the current App is not packaged with the native recording plug-in [{1}]"
|
||||
,"SCW9:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="提供的RecordApp.UniNativeUtsPlugin值不是RecordApp的uts原生录音插件"
|
||||
//@@en="The provided RecordApp.UniNativeUtsPlugin value is not RecordApp’s uts native recording plug-in"
|
||||
,"TGMm:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需在App逻辑层中调用原生插件功能"
|
||||
//@@en="The native plug-in function needs to be called in the App logic layer"
|
||||
,"MrBx:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未开始录音,不可以调用{1}"
|
||||
//@@en="Recording has not started and {1} cannot be called"
|
||||
,"0FGq:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需先调用RecordApp.UniWebViewActivate,然后才可以调用RequestPermission"
|
||||
//@@en="RecordApp.UniWebViewActivate needs to be called first, and then RequestPermission can be called"
|
||||
,"PkQ2:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="不应当出现的非H5权限请求"
|
||||
//@@en="Non-H5 permission requests that should not appear"
|
||||
,"Jk72:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="正在调用plus.ios@AVAudioSession请求iOS原生录音权限"
|
||||
//@@en="Calling plus.ios@AVAudioSession to request iOS native recording permissions"
|
||||
,"Y3rC:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="项目配置中未声明iOS录音权限{1}"
|
||||
//@@en="iOS recording permission {1} is not declared in the project configuration"
|
||||
,"9xoE:"+ //args: {1}
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="已获得iOS原生录音权限"
|
||||
//@@en="Obtained iOS native recording permissions"
|
||||
,"j15C:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="plus.ios请求录音权限,状态值: "
|
||||
//@@en="plus.ios requests recording permission, status value: "
|
||||
,"iKhe:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="正在调用plus.android.requestPermissions请求Android原生录音权限"
|
||||
//@@en="Calling plus.android.requestPermissions to request Android native recording permissions"
|
||||
,"7Noe:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="已获得Android原生录音权限:"
|
||||
//@@en="Obtained Android native recording permission: "
|
||||
,"Bgls:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="plus.android请求录音权限:无权限"
|
||||
//@@en="plus.android requests recording permission: No permission"
|
||||
,"Ruxl:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="plus.android请求录音权限出错:"
|
||||
//@@en="plus.android error in requesting recording permission: "
|
||||
,"0JQw:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="调用plus的权限请求出错:"
|
||||
//@@en="An error occurred in the permission request to call plus: "
|
||||
,"Mvl7:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="用户拒绝了录音权限"
|
||||
//@@en="User denied recording permission"
|
||||
,"0caE:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="正在调用原生插件请求录音权限"
|
||||
//@@en="Calling the native plug-in to request recording permission"
|
||||
,"Lx5r:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="已获得录音权限"
|
||||
//@@en="Recording permission obtained"
|
||||
,"Lx6r:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="无录音权限"
|
||||
//@@en="No recording permission"
|
||||
,"Lx7r:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="无法调用RequestPermission:"
|
||||
//@@en="Unable to call RequestPermission: "
|
||||
,"ksoA:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="无法连接到renderjs"
|
||||
//@@en="Unable to connect to renderjs"
|
||||
,"KnF0:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="需先调用RecordApp.UniWebViewActivate,然后才可以调用Start"
|
||||
//@@en="RecordApp.UniWebViewActivate needs to be called first, and then Start can be called"
|
||||
,"XCMU:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="不应当出现的非H5录音Start"
|
||||
//@@en="Start of non-H5 recordings that should not appear"
|
||||
,"rSLO:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="无法调用Start:"
|
||||
//@@en="Unable to call Start: "
|
||||
,"Bjx9:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未开始录音,但收到renderjs回传的onRecEncodeChunk"
|
||||
//@@en="Recording did not start, but onRecEncodeChunk returned by renderjs was received"
|
||||
,"MTdp:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未开始录音,但收到Uni Native PCM数据"
|
||||
//@@en="Recording did not start, but Uni Native PCM data was received"
|
||||
,"BjGP:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未开始录音,但收到UniNativeUtsPlugin PCM数据"
|
||||
//@@en="Recording did not start, but UniNativeUtsPlugin PCM data was received"
|
||||
,"byzO:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未开始录音"
|
||||
//@@en="Recording not started"
|
||||
,"YP4V:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="不应当出现的非H5录音Stop"
|
||||
//@@en="Stop non-H5 recordings that should not appear"
|
||||
,"TPhg:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="未开始录音"
|
||||
//@@en="Recording not started"
|
||||
,"pP4O:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="无法调用Stop:"
|
||||
//@@en="Unable to call Stop: "
|
||||
,"H6cq:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
//@@zh="不应该出现的renderjs发回的文件数据丢失"
|
||||
//@@en="The file data sent back by renderjs should not be lost"
|
||||
,"gomD:"+ //no args
|
||||
"" /** TODO: translate to your-language **/
|
||||
|
||||
]);
|
||||
//*************** End srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
|
||||
|
||||
//@@User Code-2 Begin 手写代码放这里 Put the handwritten code here @@
|
||||
|
||||
//@@User Code-2 End @@
|
||||
|
||||
}));
|
406
src/uni_modules/Recorder-UniCore/i18n/en-US.js
Normal file
406
src/uni_modules/Recorder-UniCore/i18n/en-US.js
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
Recorder ../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/i18n/en-US.js
|
||||
https://github.com/xiangyuecn/Recorder
|
||||
|
||||
Usage: Recorder.i18n.lang="en-US" or "en"
|
||||
|
||||
Desc: English, 英语。This translation mainly comes from: google translation + Baidu translation, translated from Chinese to English. 此翻译主要来自:google翻译+百度翻译,由中文翻译成英文。
|
||||
|
||||
注意:请勿修改//@@打头的文本行;以下代码结构由/src/package-i18n.js自动生成,只允许在字符串中填写翻译后的文本,请勿改变代码结构;翻译的文本如果需要明确的空值,请填写"=Empty";文本中的变量用{n}表示(n代表第几个变量),所有变量必须都出现至少一次,如果不要某变量用{n!}表示
|
||||
|
||||
Note: Do not modify the text lines starting with //@@; The following code structure is automatically generated by /src/package-i18n.js, only the translated text is allowed to be filled in the string, please do not change the code structure; If the translated text requires an explicit empty value, please fill in "=Empty"; Variables in the text are represented by {n} (n represents the number of variables), all variables must appear at least once, if a variable is not required, it is represented by {n!}
|
||||
*/
|
||||
(function(factory){
|
||||
var browser=typeof window=="object" && !!window.document;
|
||||
var win=browser?window:Object; //非浏览器环境,Recorder挂载在Object下面
|
||||
factory(win.Recorder,browser);
|
||||
}(function(Recorder,isBrowser){
|
||||
"use strict";
|
||||
var i18n=Recorder.i18n;
|
||||
|
||||
//@@User Code-1 Begin 手写代码放这里 Put the handwritten code here @@
|
||||
|
||||
//@@User Code-1 End @@
|
||||
|
||||
//@@Exec i18n.lang="en-US";
|
||||
Recorder.CLog('Import Page[Recorder_UniCore] lang="en-US"');
|
||||
|
||||
//@@Exec i18n.alias["en-US"]="en";
|
||||
|
||||
var putSet={lang:"en"};
|
||||
|
||||
//@@Exec i18n.data["rtl$en"]=false;
|
||||
i18n.data["desc-page-Recorder_UniCore$en"]="English, 英语。This translation mainly comes from: google translation + Baidu translation, translated from Chinese to English. 此翻译主要来自:google翻译+百度翻译,由中文翻译成英文。";
|
||||
//@@Exec i18n.GenerateDisplayEnglish=false;
|
||||
|
||||
|
||||
|
||||
//*************** Begin srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
|
||||
i18n.put(putSet,
|
||||
[ //@@PutList
|
||||
|
||||
//@@zh="微信小程序中需要:{1}"
|
||||
//@@Put0
|
||||
"RXs7:"+ //args: {1}
|
||||
"WeChat miniProgram requires: {1}"
|
||||
|
||||
//@@zh="Recorder-UniCore目前只支持:H5、APP(Android iOS)、MP-WEIXIN,其他平台环境需要自行编写适配文件实现接入"
|
||||
,"4ATo:"+ //no args
|
||||
"Recorder-UniCore currently only supports: H5, APP (Android iOS), MP-WEIXIN, other platform environments need to write their own adaptation files to achieve access"
|
||||
|
||||
//@@zh="RecordApp.UniWebViewActivate 需要传入当前页面或组件的this对象作为参数"
|
||||
,"GwCz:"+ //no args
|
||||
"RecordApp.UniWebViewActivate needs to pass in the this object of the current page or component as a parameter"
|
||||
|
||||
//@@zh="RecordApp.UniWebViewActivate 发生不应该出现的错误(可能需要升级插件代码):"
|
||||
,"ipB3:"+ //no args
|
||||
"An error occurred in RecordApp.UniWebViewActivate that should not occur (the plug-in code may need to be upgraded): "
|
||||
|
||||
//@@zh="RecordApp.UniWebViewActivate 已切换当前页面或组件的renderjs所在的WebView"
|
||||
,"WpKg:"+ //no args
|
||||
"RecordApp.UniWebViewActivate has switched the WebView where the renderjs of the current page or component is located"
|
||||
|
||||
//@@zh="RecordApp.UniRenderjsRegister 发生不应该出现的错误(可能需要升级插件代码):"
|
||||
,"Uc9E:"+ //no args
|
||||
"An error occurred in RecordApp.UniRenderjsRegister that should not occur (the plugin code may need to be upgraded): "
|
||||
|
||||
//@@zh="RecordApp.UniRenderjsRegister 重复注册当前页面renderjs模块,一个组件内只允许一个renderjs模块进行注册"
|
||||
,"mzKj:"+ //no args
|
||||
"RecordApp.UniRenderjsRegister repeatedly registers the renderjs module of the current page. Only one renderjs module is allowed to be registered in a component"
|
||||
|
||||
//@@zh="RecordApp.UniRenderjsRegister 已注册当前页面renderjs模块"
|
||||
,"7kJS:"+ //no args
|
||||
"RecordApp.UniRenderjsRegister has registered the renderjs module of the current page"
|
||||
|
||||
//@@zh="严重兼容性问题:无法获取页面或组件this.$root.$scope或.$page"
|
||||
,"KpY6:"+ //no args
|
||||
"Serious compatibility issue: Unable to get page or component this.$root.$scope or .$page"
|
||||
|
||||
//@@zh="需要先调用RecordApp.UniWebViewActivate方法"
|
||||
,"AGd7:"+ //no args
|
||||
"You need to call the RecordApp.UniWebViewActivate method first"
|
||||
|
||||
//@@zh="需先调用RecordApp.RequestPermission方法"
|
||||
,"7ot0:"+ //no args
|
||||
"You need to call the RecordApp.RequestPermission method first"
|
||||
|
||||
//@@zh="需重新调用RecordApp.RequestPermission方法"
|
||||
,"VsdN:"+ //no args
|
||||
"The RecordApp.RequestPermission method needs to be called again"
|
||||
|
||||
//@@zh="检测到有其他页面或组件调用了RecordApp.UniPageOnShow(WvCid={1}),但未调用过RecordApp.UniWebViewActivate(当前WvCid={2}),部分功能会继续使用之前Activate的WebView和组件,请确保这是符合你的业务逻辑,不是因为忘记了调用UniWebViewActivate"
|
||||
,"SWsy:"+ //args: {1}-{2}
|
||||
"It is detected that another page or component has called RecordApp.UniPageOnShow (WvCid={1}), but RecordApp.UniWebViewActivate (current WvCid={2}) has not been called. Some functions will continue to use the previously Activated WebView and components. Please make sure This is in line with your business logic, not because you forgot to call UniWebViewActivate"
|
||||
|
||||
//@@zh="{1}未正确查询到节点,将使用传入的当前页面或组件this的$el.parentNode作为组件根节点。如果template下存在多个根节点(vue3 multi-root),尽量在最外面再套一层view来避免兼容性问题"
|
||||
,"dX7B:"+ //args: {1}
|
||||
"{1} does not query the node correctly, and will use the current page or component this's $el.parentNode as the component root node. If there are multiple root nodes under the template (vue3 multi-root), try to add another layer of view on the outermost to avoid compatibility issues"
|
||||
|
||||
//@@zh="{1}需在renderjs中调用并且传入当前模块的this"
|
||||
,"dX5B:"+ //args: {1}
|
||||
"{1} needs to be called in renderjs and pass in this of the current module"
|
||||
|
||||
//@@zh="{1}需要传入当前页面或组件的this对象作为参数"
|
||||
,"dX6B:"+ //args: {1}
|
||||
"{1} needs to pass in the this object of the current page or component as a parameter"
|
||||
|
||||
//@@zh="当前不是App逻辑层"
|
||||
,"TfJX:"+ //no args
|
||||
"Currently it is not the App logic layer"
|
||||
|
||||
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
|
||||
,"peIm:"+ //no args
|
||||
"RecordApp.UniWebViewActivate has not been called yet"
|
||||
|
||||
//@@zh="未找到此页面renderjs所在的WebView"
|
||||
,"qDo1:"+ //no args
|
||||
"The WebView where renderjs for this page is not found"
|
||||
|
||||
//@@zh=",不可以调用RecordApp.UniWebViewEval"
|
||||
,"igw2:"+ //no args
|
||||
", RecordApp.UniWebViewEval cannot be called"
|
||||
|
||||
//@@zh="当前不是App逻辑层"
|
||||
,"lU1W:"+ //no args
|
||||
"Currently it is not the App logic layer"
|
||||
|
||||
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
|
||||
,"mSbR:"+ //no args
|
||||
"RecordApp.UniWebViewActivate has not been called yet"
|
||||
|
||||
//@@zh="未找到此页面renderjs所在的WebView Cid"
|
||||
,"6Iql:"+ //no args
|
||||
"The WebView Cid where renderjs for this page is not found"
|
||||
|
||||
//@@zh=",不可以调用RecordApp.UniWebViewVueCall"
|
||||
,"TtoS:"+ //no args
|
||||
", RecordApp.UniWebViewVueCall cannot be called"
|
||||
|
||||
//@@zh="renderjs中未import导入RecordApp"
|
||||
,"U1Be:"+ //no args
|
||||
"RecordApp is not imported in renderjs"
|
||||
|
||||
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister"
|
||||
,"Bcgi:"+ //no args
|
||||
"RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs"
|
||||
|
||||
//@@zh="没有找到组件的renderjs模块"
|
||||
,"URyD:"+ //no args
|
||||
"The renderjs module for the component was not found"
|
||||
|
||||
//@@zh="{1}连接renderjs超时"
|
||||
,"KQhJ:"+ //args: {1}
|
||||
"{1} connection renderjs timeout"
|
||||
|
||||
//@@zh="{1}处理超时"
|
||||
,"RDcZ:"+ //args: {1}
|
||||
"{1} processing timeout"
|
||||
|
||||
//@@zh="需要在页面中提供一个renderjs,在里面import导入RecordApp、录音格式编码器、可视化插件等"
|
||||
,"TSmQ:"+ //no args
|
||||
"You need to provide a renderjs in the page, and import RecordApp, recording format encoder, visualization plug-in, etc."
|
||||
|
||||
//@@zh="需在renderjs中import {1}"
|
||||
,"AN0e:"+ //args: {1}
|
||||
"Need to import {1} in renderjs"
|
||||
|
||||
//@@zh="不应该出现的MainReceiveBind重复绑定"
|
||||
,"vEgr:"+ //no args
|
||||
"MainReceiveBind duplicate binding that should not occur"
|
||||
|
||||
//@@zh="从renderjs发回数据但UniMainCallBack回调不存在:"
|
||||
,"kZx6:"+ //no args
|
||||
"Sending data back from renderjs but UniMainCallBack callback does not exist: "
|
||||
|
||||
//@@zh="[MainReceive]从renderjs发回未知数据:"
|
||||
,"ZHwv:"+ //no args
|
||||
"[MainReceive] Unknown data sent back from renderjs: "
|
||||
|
||||
//@@zh="只允许在renderjs中调用RecordApp.UniWebViewSendBigBytesToMain"
|
||||
,"MujG:"+ //no args
|
||||
"Only allowed to call RecordApp.UniWebViewSendBigBytesToMain in renderjs"
|
||||
|
||||
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister才能调用RecordApp.UniWebViewSendBigBytesToMain"
|
||||
,"kE91:"+ //no args
|
||||
"RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs to call RecordApp.UniWebViewSendBigBytesToMain"
|
||||
|
||||
//@@zh="无效的BigBytes回传数据"
|
||||
,"CjMb:"+ //no args
|
||||
"Invalid BigBytes return data"
|
||||
|
||||
//@@zh="保存文件{1}失败:"
|
||||
,"UqfI:"+ //args: {1}
|
||||
"Failed to save file {1}: "
|
||||
|
||||
//@@zh="当前环境未支持保存本地文件"
|
||||
,"kxOd:"+ //no args
|
||||
"The current environment does not support saving local files"
|
||||
|
||||
//@@zh=" | RecordApp的uni-app支持文档和示例:{1} "
|
||||
,"1f2V:"+ //args: {1}
|
||||
" | RecordApp’s uni-app support documentation and examples: {1}"
|
||||
|
||||
//@@zh="当前录音由原生录音插件提供支持"
|
||||
,"XSYY:"+ //no args
|
||||
"Current recording is powered by native recording plug-in"
|
||||
|
||||
//@@zh="当前录音由uts插件提供支持"
|
||||
,"nnM6:"+ //no args
|
||||
"Current recording is powered by uts plugin"
|
||||
|
||||
//@@zh="当前已配置RecordApp.UniWithoutAppRenderjs,必须提供原生录音插件或uts插件才能录音,请参考RecordApp.UniNativeUtsPlugin配置"
|
||||
,"fqhr:"+ //no args
|
||||
"RecordApp.UniWithoutAppRenderjs is currently configured. A native recording plug-in or uts plug-in must be provided to record. Please refer to the RecordApp.UniNativeUtsPlugin configuration"
|
||||
|
||||
//@@zh="当前RecordApp运行在逻辑层中(性能会略低一些,可视化等插件不可用)"
|
||||
,"xYRb:"+ //no args
|
||||
"Currently RecordApp runs in the logical layer (performance will be slightly lower, and plug-ins such as visualization are not available) "
|
||||
|
||||
//@@zh="未找到当前页面renderjs所在的WebView"
|
||||
,"S3eF:"+ //no args
|
||||
"The WebView where renderjs of the current page is located is not found"
|
||||
|
||||
//@@zh="当前RecordApp运行在renderjs所在的WebView中(逻辑层中只能做有限的实时处理,可视化等插件均需要在renderjs中进行调用)"
|
||||
,"0hyi:"+ //no args
|
||||
"The current RecordApp runs in the WebView where renderjs is located (only limited real-time processing can be done in the logic layer, and visualization and other plug-ins need to be called in renderjs) "
|
||||
|
||||
//@@zh=",请检查此页面代码中是否编写了lang=renderjs的module,并且调用了RecordApp.UniRenderjsRegister;如果确实没有renderjs,比如nvue页面,请设置RecordApp.UniWithoutAppRenderjs=true并且搭配配套的原生插件在逻辑层中直接录音"
|
||||
,"e6Mo:"+ //no args
|
||||
", please check whether the module with lang=renderjs is written in the code of this page and RecordApp.UniRenderjsRegister is called; if there is indeed no renderjs, such as nvue page, please set RecordApp.UniWithoutAppRenderjs=true and use the matching native plug-in to record directly in the logic layer"
|
||||
|
||||
//@@zh="【在App内使用{1}的授权许可】"
|
||||
,"FabE:"+ //args: {1}
|
||||
"[License for use of {1} within the App] "
|
||||
|
||||
//@@zh="已购买原生录音插件,获得授权许可"
|
||||
,"w37G:"+ //no args
|
||||
"Purchased the native recording plug-in and obtained the license"
|
||||
|
||||
//@@zh="已购买uts插件,获得授权许可"
|
||||
,"e71S:"+ //no args
|
||||
"Purchased uts plug-in and obtained license"
|
||||
|
||||
//@@zh="UniAppUseLicense填写无效,如果已获取到了商用授权,请填写:{1},否则请使用空字符串"
|
||||
,"aPoj:"+ //args: {1}
|
||||
"UniAppUseLicense is invalid. If you have obtained a commercial license, please fill in: {1}, otherwise please use an empty string"
|
||||
|
||||
//@@zh="未找到Canvas:{1},请确保此DOM已挂载(可尝试用$nextTick等待DOM更新)"
|
||||
,"k7im:"+ //args: {1}
|
||||
"Canvas not found: {1}, please make sure this DOM is mounted (try $nextTick to wait for DOM update) "
|
||||
|
||||
//@@zh="RecordApp.UniFindCanvas未适配当前环境"
|
||||
,"yI24:"+ //no args
|
||||
"RecordApp.UniFindCanvas does not adapt to the current environment"
|
||||
|
||||
//@@zh="未配置RecordApp.UniNativeUtsPlugin原生录音插件"
|
||||
,"H753:"+ //no args
|
||||
"RecordApp.UniNativeUtsPlugin native recording plug-in is not configured"
|
||||
|
||||
//@@zh="renderjs中不支持设置RecordApp.UniNativeUtsPlugin"
|
||||
,"l6sY:"+ //no args
|
||||
"Setting RecordApp.UniNativeUtsPlugin is not supported in renderjs"
|
||||
|
||||
//@@zh="当前App未打包进双端原生插件[{1}],尝试加载单端[{2}]"
|
||||
,"kSjQ:"+ //args: {1}-{2}
|
||||
"The current App is not packaged into the dual-end native plug-in [{1}], try to load the single-end [{2}]"
|
||||
|
||||
//@@zh="已加载原生录音插件[{1}]"
|
||||
,"Xh1W:"+ //args: {1}
|
||||
"Native recording plugin loaded [{1}]"
|
||||
|
||||
//@@zh="配置了RecordApp.UniNativeUtsPlugin,但当前App未打包进原生录音插件[{1}]"
|
||||
,"SCW9:"+ //args: {1}
|
||||
"RecordApp.UniNativeUtsPlugin is configured, but the current App is not packaged with the native recording plug-in [{1}]"
|
||||
|
||||
//@@zh="提供的RecordApp.UniNativeUtsPlugin值不是RecordApp的uts原生录音插件"
|
||||
,"TGMm:"+ //no args
|
||||
"The provided RecordApp.UniNativeUtsPlugin value is not RecordApp’s uts native recording plug-in"
|
||||
|
||||
//@@zh="需在App逻辑层中调用原生插件功能"
|
||||
,"MrBx:"+ //no args
|
||||
"The native plug-in function needs to be called in the App logic layer"
|
||||
|
||||
//@@zh="未开始录音,不可以调用{1}"
|
||||
,"0FGq:"+ //args: {1}
|
||||
"Recording has not started and {1} cannot be called"
|
||||
|
||||
//@@zh="需先调用RecordApp.UniWebViewActivate,然后才可以调用RequestPermission"
|
||||
,"PkQ2:"+ //no args
|
||||
"RecordApp.UniWebViewActivate needs to be called first, and then RequestPermission can be called"
|
||||
|
||||
//@@zh="不应当出现的非H5权限请求"
|
||||
,"Jk72:"+ //no args
|
||||
"Non-H5 permission requests that should not appear"
|
||||
|
||||
//@@zh="正在调用plus.ios@AVAudioSession请求iOS原生录音权限"
|
||||
,"Y3rC:"+ //no args
|
||||
"Calling plus.ios@AVAudioSession to request iOS native recording permissions"
|
||||
|
||||
//@@zh="项目配置中未声明iOS录音权限{1}"
|
||||
,"9xoE:"+ //args: {1}
|
||||
"iOS recording permission {1} is not declared in the project configuration"
|
||||
|
||||
//@@zh="已获得iOS原生录音权限"
|
||||
,"j15C:"+ //no args
|
||||
"Obtained iOS native recording permissions"
|
||||
|
||||
//@@zh="plus.ios请求录音权限,状态值: "
|
||||
,"iKhe:"+ //no args
|
||||
"plus.ios requests recording permission, status value: "
|
||||
|
||||
//@@zh="正在调用plus.android.requestPermissions请求Android原生录音权限"
|
||||
,"7Noe:"+ //no args
|
||||
"Calling plus.android.requestPermissions to request Android native recording permissions"
|
||||
|
||||
//@@zh="已获得Android原生录音权限:"
|
||||
,"Bgls:"+ //no args
|
||||
"Obtained Android native recording permission: "
|
||||
|
||||
//@@zh="plus.android请求录音权限:无权限"
|
||||
,"Ruxl:"+ //no args
|
||||
"plus.android requests recording permission: No permission"
|
||||
|
||||
//@@zh="plus.android请求录音权限出错:"
|
||||
,"0JQw:"+ //no args
|
||||
"plus.android error in requesting recording permission: "
|
||||
|
||||
//@@zh="调用plus的权限请求出错:"
|
||||
,"Mvl7:"+ //no args
|
||||
"An error occurred in the permission request to call plus: "
|
||||
|
||||
//@@zh="用户拒绝了录音权限"
|
||||
,"0caE:"+ //no args
|
||||
"User denied recording permission"
|
||||
|
||||
//@@zh="正在调用原生插件请求录音权限"
|
||||
,"Lx5r:"+ //no args
|
||||
"Calling the native plug-in to request recording permission"
|
||||
|
||||
//@@zh="已获得录音权限"
|
||||
,"Lx6r:"+ //no args
|
||||
"Recording permission obtained"
|
||||
|
||||
//@@zh="无录音权限"
|
||||
,"Lx7r:"+ //no args
|
||||
"No recording permission"
|
||||
|
||||
//@@zh="无法调用RequestPermission:"
|
||||
,"ksoA:"+ //no args
|
||||
"Unable to call RequestPermission: "
|
||||
|
||||
//@@zh="无法连接到renderjs"
|
||||
,"KnF0:"+ //no args
|
||||
"Unable to connect to renderjs"
|
||||
|
||||
//@@zh="需先调用RecordApp.UniWebViewActivate,然后才可以调用Start"
|
||||
,"XCMU:"+ //no args
|
||||
"RecordApp.UniWebViewActivate needs to be called first, and then Start can be called"
|
||||
|
||||
//@@zh="不应当出现的非H5录音Start"
|
||||
,"rSLO:"+ //no args
|
||||
"Start of non-H5 recordings that should not appear"
|
||||
|
||||
//@@zh="无法调用Start:"
|
||||
,"Bjx9:"+ //no args
|
||||
"Unable to call Start: "
|
||||
|
||||
//@@zh="未开始录音,但收到renderjs回传的onRecEncodeChunk"
|
||||
,"MTdp:"+ //no args
|
||||
"Recording did not start, but onRecEncodeChunk returned by renderjs was received"
|
||||
|
||||
//@@zh="未开始录音,但收到Uni Native PCM数据"
|
||||
,"BjGP:"+ //no args
|
||||
"Recording did not start, but Uni Native PCM data was received"
|
||||
|
||||
//@@zh="未开始录音,但收到UniNativeUtsPlugin PCM数据"
|
||||
,"byzO:"+ //no args
|
||||
"Recording did not start, but UniNativeUtsPlugin PCM data was received"
|
||||
|
||||
//@@zh="未开始录音"
|
||||
,"YP4V:"+ //no args
|
||||
"Recording not started"
|
||||
|
||||
//@@zh="不应当出现的非H5录音Stop"
|
||||
,"TPhg:"+ //no args
|
||||
"Stop non-H5 recordings that should not appear"
|
||||
|
||||
//@@zh="未开始录音"
|
||||
,"pP4O:"+ //no args
|
||||
"Recording not started"
|
||||
|
||||
//@@zh="无法调用Stop:"
|
||||
,"H6cq:"+ //no args
|
||||
"Unable to call Stop: "
|
||||
|
||||
//@@zh="不应该出现的renderjs发回的文件数据丢失"
|
||||
,"gomD:"+ //no args
|
||||
"The file data sent back by renderjs should not be lost"
|
||||
|
||||
]);
|
||||
//*************** End srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
|
||||
|
||||
//@@User Code-2 Begin 手写代码放这里 Put the handwritten code here @@
|
||||
|
||||
//@@User Code-2 End @@
|
||||
|
||||
}));
|
19
src/uni_modules/Recorder-UniCore/license.md
Normal file
19
src/uni_modules/Recorder-UniCore/license.md
Normal file
@ -0,0 +1,19 @@
|
||||
《许可及服务协议》
|
||||
|
||||
**您(以下称“用户”)下载、使用我(以下称“作者”)提供的Recorder-UniCore组件(含原生录音插件、uts插件,以下统称“本组件”),应当阅读并遵守本许可协议。请用户务必审慎阅读、充分理解各条款内容,特别是免除或者限制责任的条款,并选择接受或不接受。除非用户已阅读并接受本协议所有条款,否则用户无权下载、使用本组件及相关服务,用户的下载、使用等行为即视为用户已阅读并同意本许可协议的约束。**
|
||||
|
||||
1. 用户应当直接从作者许可的途径,如作者的GitHub、Gitee仓库、已上架的DCloud插件市场、QQ群等途径中获取本组件;其他途径获取到的组件代码是未经过作者授权的,存在安全隐患,可能会导致你的程序、资产受到侵害,作者对因此给用户造成的损失不予负责。
|
||||
|
||||
2. 作者将积极并采取措施保护用户的信息和隐私;组件本身不会搜集存储任何用户信息。
|
||||
|
||||
3. 除法律法规有明确规定外,作者将尽最大努力确保本组件及其所涉及的技术及信息安全、有效、准确、可靠,但受限于现有技术,用户理解作者不能对此进行担保。
|
||||
|
||||
4. 用户理解,对于不可抗力及第三方原因导致的您的直接或间接损失,作者无法承担责任。
|
||||
|
||||
5. 用户因使用本组件进行生成、处理数据,由此引起或与有关的包括但不限于利润损失、资料损失、业务中断的损害赔偿或其它商业损害赔偿或损失,需由用户自行承担。
|
||||
|
||||
6. 如若发生赔偿、退款等行为,赔偿、退款等累计金额不得超过用户实际支付给作者的总金额。
|
||||
|
||||
7. 已授予的授权许可,包括免费授权,和已购买的原生录音插件、uts插件,均仅限在授权指定的uni-app的应用标识(AppID)对应的项目上使用,不可在其他项目上使用;用户不得对本组件及其中的相关信息擅自出租、出借、销售、逆向工程、破解,不得在未取得作者授权的情况下借助本组件发展与本组件有关联的衍生软件产品、服务、插件、外挂等。
|
||||
|
||||
8. 用户不得使用本组件从事违反法律法规政策、破坏公序良俗、损害公共利益的行为。
|
88
src/uni_modules/Recorder-UniCore/package.json
Normal file
88
src/uni_modules/Recorder-UniCore/package.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"id": "Recorder-UniCore",
|
||||
"displayName": "跨平台Recorder录音插件:支持多种格式、音频可视化、实时上传、语音识别",
|
||||
"version": "1.0.250331",
|
||||
"description": "支持H5、Android iOS App、微信小程序;mp3 wav pcm g711a g711u ogg amr 录音格式;实时帧回调处理 音频转码 波形动画显示 ASR语音转文字 无录制时长限制",
|
||||
"keywords": [
|
||||
"Recorder-UniCore",
|
||||
"recorder-core",
|
||||
"RecordApp",
|
||||
"record",
|
||||
"recording"
|
||||
],
|
||||
"repository": "https://github.com/xiangyuecn/Recorder",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.6.11"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": "753610399"
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "录音权限"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y",
|
||||
"app-uvue": "n",
|
||||
"app-harmony": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "n",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "n",
|
||||
"百度": "n",
|
||||
"字节跳动": "n",
|
||||
"QQ": "n",
|
||||
"钉钉": "n",
|
||||
"快手": "n",
|
||||
"飞书": "n",
|
||||
"京东": "n"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
425
src/uni_modules/Recorder-UniCore/readme.md
Normal file
425
src/uni_modules/Recorder-UniCore/readme.md
Normal file
@ -0,0 +1,425 @@
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
# Recorder-UniCore组件:uni-app内使用RecordApp录音
|
||||
|
||||
本组件使用`Recorder`开源库来进行录音和音频数据处理,使用`RecordApp`和本组件内的`app-uni-support.js`来适配到不同平台环境下进行录音。
|
||||
|
||||
- 支持vue2、vue3、nvue
|
||||
- 支持编译成:H5、Android App、iOS App、微信小程序
|
||||
- 支持已有的大部分录音格式:mp3、wav、pcm、amr、ogg、g711a、g711u等
|
||||
- 支持实时处理,包括变速变调、实时上传、ASR语音转文字
|
||||
- 支持可视化波形显示;可配置回声消除、降噪;**注意:不支持通话时录音**
|
||||
- 支持PCM音频流式播放、完整播放,App端用原生插件边录音边播放更流畅
|
||||
- 支持离线使用,本组件和配套原生插件均不依赖网络
|
||||
- App端有配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin)可供搭配使用,兼容性和体验更好
|
||||
|
||||
**详细文档(含Demo项目):** [https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp](https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp)
|
||||
|
||||
**Recorder开源库地址:** [https://github.com/xiangyuecn/Recorder](https://github.com/xiangyuecn/Recorder)
|
||||
|
||||
如果github打不开,可以[点此访问Gitee仓库地址](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp) 。
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
## 测试方法
|
||||
**示例项目如果在HBuilder中编译失败,请删掉node_modules目录重新手动执行npm install(偶尔出现HBuilder自动创建项目依赖包不完整,导致无法编译)**
|
||||
|
||||
1. 在本插件市场页面右侧下载或导入示例项目(或打开上面详细文档链接中的Demo源码)
|
||||
2. 在测试项目根目录执行 `npm install --registry=https://registry.npmmirror.com/` ,完成`recorder-core`依赖的安装
|
||||
3. 在HBuilder中打开本测试项目文件夹
|
||||
4. 在HBuilder中运行到浏览器、手机、微信小程序,即可在不同环境下测试
|
||||
5. 测试中提供了:基础录音、播放、上传、WebSocket实时语音通话对讲、ASR语音识别等功能
|
||||
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
# 集成到自己项目中
|
||||
|
||||
你可以直接参考上面的测试示例项目源码,里面的`main_recTest.vue`更容易入门;示例项目中已经实现了很多功能,简单使用可直接照抄Demo代码到你的项目中。
|
||||
|
||||
|
||||
## 一、引入js文件
|
||||
1. 在你的项目根目录安装`recorder-core`:`npm install recorder-core --registry=https://registry.npmmirror.com/`
|
||||
2. 导入Recorder-UniCore组件:插件市场下载本组件,然后添加到你的项目中 `/uni_modules/Recorder-UniCore`
|
||||
3. 项目配置好录音权限,参考下面的录音权限配置章节,**特别注意App后台录音配置、小程序权限声明**
|
||||
4. 在需要录音的vue文件script内编写以下代码,按需引入需要的js
|
||||
|
||||
``` html
|
||||
<template>
|
||||
<view>
|
||||
... 建议template下只有一个根节点(最外面套一层view),如果不小心踩到了vue3的Fragments(multi-root 多个根节点)特性(vue2编译会报错,vue3不会),可能会出现奇奇怪怪的兼容性问题
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script> /**这里是逻辑层**/
|
||||
//必须引入的Recorder核心(文件路径是 /src/recorder-core.js 下同),使用import、require都行
|
||||
import Recorder from 'recorder-core' //注意如果未引用Recorder变量,可能编译时会被优化删除(如vue3 tree-shaking),请改成 import 'recorder-core',或随便调用一下 Recorder.a=1 保证强引用
|
||||
|
||||
//必须引入的RecordApp核心文件(文件路径是 /src/app-support/app.js)
|
||||
import RecordApp from 'recorder-core/src/app-support/app'
|
||||
|
||||
//所有平台必须引入的uni-app支持文件(如果编译出现路径错误,请把@换成 ../../ 这种)
|
||||
import '@/uni_modules/Recorder-UniCore/app-uni-support.js'
|
||||
|
||||
/** 需要编译成微信小程序时,引入微信小程序支持文件 **/
|
||||
// #ifdef MP-WEIXIN
|
||||
import 'recorder-core/src/app-support/app-miniProgram-wx-support.js'
|
||||
// #endif
|
||||
|
||||
|
||||
/** H5、小程序环境中:引入需要的格式编码器、可视化插件,App环境中在renderjs中引入 **/
|
||||
// 注意:如果App中需要在逻辑层中调用Recorder的编码/转码功能,需要去掉此条件编译,否则会报未加载编码器的错误
|
||||
// #ifdef H5 || MP-WEIXIN
|
||||
//按需引入你需要的录音格式支持文件,如果需要多个格式支持,把这些格式的编码引擎js文件统统引入进来即可
|
||||
import 'recorder-core/src/engine/mp3'
|
||||
import 'recorder-core/src/engine/mp3-engine' //如果此格式有额外的编码引擎(*-engine.js)的话,必须要加上
|
||||
|
||||
//可选的插件支持项,把需要的插件按需引入进来即可
|
||||
import 'recorder-core/src/extensions/waveview'
|
||||
// #endif
|
||||
|
||||
// ... 这后面写页面代码,用选项式API风格(vue2、vue3)、setup组合式API风格(仅vue3)都可以
|
||||
</script>
|
||||
```
|
||||
|
||||
5. 编译成app时,默认需要额外提供一个renderjs模块,请照抄下面这段代码放到vue文件末尾
|
||||
|
||||
``` html
|
||||
<!-- #ifdef APP -->
|
||||
<script module="yourModuleName" lang="renderjs"> //此模块内部只能用选项式API风格,vue2、vue3均可用,请照抄这段代码;不可改成setup组合式API风格,否则可能不能import vue导致编译失败
|
||||
/**需要编译成App时,你需要添加一个renderjs模块,然后一模一样的import上面那些js(微信的js除外)
|
||||
,因为App中默认是在renderjs(WebView)中进行录音和音频编码
|
||||
。如果配置了 RecordApp.UniWithoutAppRenderjs=true 且未调用依赖renderjs的功能时(如nvue、可视化、仅H5中可用的插件)
|
||||
,可不提供此renderjs模块,同时逻辑层中需要将相关import的条件编译去掉**/
|
||||
import 'recorder-core'
|
||||
import RecordApp from 'recorder-core/src/app-support/app'
|
||||
import '../../uni_modules/Recorder-UniCore/app-uni-support.js' //renderjs中似乎不支持"@/"打头的路径,如果编译路径错误请改正路径即可
|
||||
|
||||
//按需引入你需要的录音格式支持文件,和插件
|
||||
import 'recorder-core/src/engine/mp3'
|
||||
import 'recorder-core/src/engine/mp3-engine'
|
||||
|
||||
import 'recorder-core/src/extensions/waveview'
|
||||
|
||||
export default {
|
||||
mounted(){
|
||||
//App的renderjs必须调用的函数,传入当前模块this
|
||||
RecordApp.UniRenderjsRegister(this);
|
||||
},
|
||||
methods: {
|
||||
//这里定义的方法,在逻辑层中可通过 RecordApp.UniWebViewVueCall(this,'this.xxxFunc()') 直接调用
|
||||
//调用逻辑层的方法,请直接用 this.$ownerInstance.callMethod("xxxFunc",{args}) 调用,二进制数据需转成base64来传递
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
```
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
## 二、调用录音
|
||||
``` javascript
|
||||
/**在逻辑层中编写**/
|
||||
//import ... 上面那些import代码
|
||||
|
||||
//var vue3This=getCurrentInstance().proxy; //当用vue3 setup组合式 API (Composition API) 编写时,直接在import后面取到当前实例this,在需要this的地方传vue3This变量即可,其他的和选项式 API (Options API) 没有任何区别;import {getCurrentInstance} from 'vue';详细可以参考Demo项目中的 page_vue3____composition_api.vue
|
||||
|
||||
//RecordApp.UniNativeUtsPlugin={ nativePlugin:true }; //App中启用配套的原生录音插件支持,配置后会使用原生插件进行录音,没有原生插件时依旧使用renderjs H5录音
|
||||
//App中提升后台录音的稳定性:配置了原生插件后,可配置 `RecordApp.UniWithoutAppRenderjs=true` 禁用renderjs层音频编码(WebWorker加速),变成逻辑层中直接编码(但会降低逻辑层性能),后台运行时可避免部分手机WebView运行受限的影响
|
||||
//App中提升后台录音的稳定性:需要启用后台录音保活服务(iOS不需要,参考录音权限配置),Android 9开始,锁屏或进入后台一段时间后App可能会被禁止访问麦克风导致录音静音、无法录音(renderjs中H5录音也受影响),请调用配套原生插件的`androidNotifyService`接口,或使用第三方保活插件
|
||||
|
||||
export default {
|
||||
data() { return {} } //视图没有引用到的变量无需放data里,直接this.xxx使用
|
||||
|
||||
,mounted() {
|
||||
this.isMounted=true;
|
||||
//页面onShow时【必须调用】的函数,传入当前组件this
|
||||
RecordApp.UniPageOnShow(this);
|
||||
}
|
||||
,onShow(){ //onShow可能比mounted先执行,页面可能还未准备好
|
||||
if(this.isMounted) RecordApp.UniPageOnShow(this);
|
||||
}
|
||||
|
||||
,methods:{
|
||||
//请求录音权限
|
||||
recReq(){
|
||||
//编译成App时提供的授权许可(编译成H5、小程序为免费授权可不填写);如果未填写授权许可,将会在App打开后第一次调用请求录音权限时,弹出“未获得商用授权时,App上仅供测试”提示框
|
||||
//RecordApp.UniAppUseLicense='我已获得UniAppID=*****的商用授权';
|
||||
|
||||
//RecordApp.RequestPermission_H5OpenSet={ audioTrackSet:{ noiseSuppression:true,echoCancellation:true,autoGainControl:true } }; //这个是Start中的audioTrackSet配置,在h5(H5、App+renderjs)中必须提前配置,因为h5中RequestPermission会直接打开录音
|
||||
|
||||
RecordApp.UniWebViewActivate(this); //App环境下必须先切换成当前页面WebView
|
||||
RecordApp.RequestPermission(()=>{
|
||||
console.log("已获得录音权限,可以开始录音了");
|
||||
},(msg,isUserNotAllow)=>{
|
||||
if(isUserNotAllow){//用户拒绝了录音权限
|
||||
//这里你应当编写代码进行引导用户给录音权限,不同平台分别进行编写
|
||||
}
|
||||
console.error("请求录音权限失败:"+msg);
|
||||
});
|
||||
}
|
||||
|
||||
//开始录音
|
||||
,recStart(){
|
||||
//Android App如果要后台录音,需要启用后台录音保活服务(iOS不需要),需使用配套原生插件、或使用第三方保活插件
|
||||
//RecordApp.UniNativeUtsPluginCallAsync("androidNotifyService",{ title:"正在录音" ,content:"正在录音中,请勿关闭App运行" }).then(()=>{...}).catch((e)=>{...}) 注意必须RecordApp.RequestPermission得到权限后调用
|
||||
|
||||
//录音配置信息
|
||||
var set={
|
||||
type:"mp3",sampleRate:16000,bitRate:16 //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎
|
||||
/*,audioTrackSet:{ //可选,如果需要同时播放声音(比如语音通话),需要打开回声消除(并不一定会生效;打开后声音可能会从听筒播放,部分环境下(如小程序、App原生插件)可调用接口切换成扬声器外放)
|
||||
//注意:H5、App+renderjs中需要在请求录音权限前进行相同配置RecordApp.RequestPermission_H5OpenSet后此配置才会生效
|
||||
echoCancellation:true,noiseSuppression:true,autoGainControl:true} */
|
||||
,onProcess:(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd)=>{
|
||||
//全平台通用:可实时上传(发送)数据,配合Recorder.SampleData方法,将buffers中的新数据连续的转换成pcm上传,或使用mock方法将新数据连续的转码成其他格式上传,可以参考Recorder文档里面的:Demo片段列表 -> 实时转码并上传-通用版;基于本功能可以做到:实时转发数据、实时保存数据、实时语音识别(ASR)等
|
||||
|
||||
//注意:App里面是在renderjs中进行实际的音频格式编码操作,此处的buffers数据是renderjs实时转发过来的,修改此处的buffers数据不会改变renderjs中buffers,所以不会改变生成的音频文件,可在onProcess_renderjs中进行修改操作就没有此问题了;如需清理buffers内存,此处和onProcess_renderjs中均需要进行清理,H5、小程序中无此限制
|
||||
//注意:如果你要用只支持在浏览器中使用的Recorder扩展插件,App里面请在renderjs中引入此扩展插件,然后在onProcess_renderjs中调用这个插件;H5可直接在这里进行调用,小程序不支持这类插件;如果调用插件的逻辑比较复杂,建议封装成js文件,这样逻辑层、renderjs中直接import,不需要重复编写
|
||||
|
||||
//H5、小程序等可视化图形绘制,直接运行在逻辑层;App里面需要在onProcess_renderjs中进行这些操作
|
||||
// #ifdef H5 || MP-WEIXIN
|
||||
if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate);
|
||||
// #endif
|
||||
|
||||
/*实时释放清理内存,用于支持长时间录音;在指定了有效的type时,编码器内部可能还会有其他缓冲,必须同时提供takeoffEncodeChunk才能清理内存,否则type需要提供unknown格式来阻止编码器内部缓冲,App的onProcess_renderjs中需要进行相同操作
|
||||
if(this.clearBufferIdx>newBufferIdx){ this.clearBufferIdx=0 } //重新录音了就重置
|
||||
for(var i=this.clearBufferIdx||0;i<newBufferIdx;i++) buffers[i]=null;
|
||||
this.clearBufferIdx=newBufferIdx; */
|
||||
}
|
||||
,onProcess_renderjs:`function(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd){
|
||||
//App中在这里修改buffers会改变生成的音频文件,但注意:buffers会先转发到逻辑层onProcess后才会调用本方法,因此在逻辑层的onProcess中需要重新修改一遍
|
||||
//本方法可以返回true,renderjs中的onProcess将开启异步模式,处理完后调用asyncEnd结束异步,注意:这里异步修改的buffers一样的不会在逻辑层的onProcess中生效
|
||||
//App中是在renderjs中进行的可视化图形绘制,因此需要写在这里,this是renderjs模块的this(也可以用This变量);如果代码比较复杂,请直接在renderjs的methods里面放个方法xxxFunc,这里直接使用this.xxxFunc(args)进行调用
|
||||
if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate);
|
||||
|
||||
/*和onProcess中一样进行释放清理内存,用于支持长时间录音
|
||||
if(this.clearBufferIdx>newBufferIdx){ this.clearBufferIdx=0 } //重新录音了就重置
|
||||
for(var i=this.clearBufferIdx||0;i<newBufferIdx;i++) buffers[i]=null;
|
||||
this.clearBufferIdx=newBufferIdx; */
|
||||
}`
|
||||
,onProcessBefore_renderjs:`function(buffers,powerLevel,duration,sampleRate,newBufferIdx){
|
||||
//App中本方法会在逻辑层onProcess之前调用,因此修改的buffers会转发给逻辑层onProcess,本方法没有asyncEnd参数不支持异步处理
|
||||
//一般无需提供本方法只用onProcess_renderjs就行,renderjs的onProcess内部调用过程:onProcessBefore_renderjs -> 转发给逻辑层onProcess -> onProcess_renderjs
|
||||
}`
|
||||
|
||||
,takeoffEncodeChunk:true?null:(chunkBytes)=>{
|
||||
//全平台通用:实时接收到编码器编码出来的音频片段数据,chunkBytes是Uint8Array二进制数据,可以实时上传(发送)出去
|
||||
//App中如果未配置RecordApp.UniWithoutAppRenderjs时,建议提供此回调,因为录音结束后会将整个录音文件从renderjs传回逻辑层,由于uni-app的逻辑层和renderjs层数据交互性能实在太拉跨了,大点的文件传输会比较慢,提供此回调后可避免Stop时产生超大数据回传
|
||||
|
||||
//App中使用原生插件时,可方便的将数据实时保存到同一文件,第一帧时append:false新建文件,后面的append:true追加到文件
|
||||
//RecordApp.UniNativeUtsPluginCallAsync("writeFile",{path:"xxx.mp3",append:回调次数!=1, dataBase64:RecordApp.UniBtoa(chunkBytes.buffer)}).then(...).catch(...)
|
||||
}
|
||||
,takeoffEncodeChunk_renderjs:true?null:`function(chunkBytes){
|
||||
//App中这里可以做一些仅在renderjs中才生效的事情,不提供也行,this是renderjs模块的this(也可以用This变量)
|
||||
}`
|
||||
|
||||
,start_renderjs:`function(){
|
||||
//App中可以放一个函数,在Start成功时renderjs中会先调用这里的代码,this是renderjs模块的this(也可以用This变量)
|
||||
//放一些仅在renderjs中才生效的事情,比如初始化,不提供也行
|
||||
}`
|
||||
,stop_renderjs:`function(arrayBuffer,duration,mime){
|
||||
//App中可以放一个函数,在Stop成功时renderjs中会先调用这里的代码,this是renderjs模块的this(也可以用This变量)
|
||||
//放一些仅在renderjs中才生效的事情,不提供也行
|
||||
}`
|
||||
};
|
||||
|
||||
RecordApp.UniWebViewActivate(this); //App环境下必须先切换成当前页面WebView
|
||||
RecordApp.Start(set,()=>{
|
||||
console.log("已开始录音");
|
||||
//【稳如老狗WDT】可选的,监控是否在正常录音有onProcess回调,如果长时间没有回调就代表录音不正常
|
||||
//var wdt=this.watchDogTimer=setInterval ... 请参考示例Demo的main_recTest.vue中的watchDogTimer实现
|
||||
|
||||
//创建音频可视化图形绘制,App环境下是在renderjs中绘制,H5、小程序等是在逻辑层中绘制,因此需要提供两段相同的代码
|
||||
//view里面放一个canvas,canvas需要指定宽高(下面style里指定了300*100)
|
||||
//<canvas type="2d" class="recwave-WaveView" style="width:300px;height:100px"></canvas>
|
||||
RecordApp.UniFindCanvas(this,[".recwave-WaveView"],`
|
||||
this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100});
|
||||
`,(canvas1)=>{
|
||||
this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100});
|
||||
});
|
||||
},(msg)=>{
|
||||
console.error("开始录音失败:"+msg);
|
||||
});
|
||||
}
|
||||
|
||||
//暂停录音
|
||||
,recPause(){
|
||||
if(RecordApp.GetCurrentRecOrNull()){
|
||||
RecordApp.Pause();
|
||||
console.log("已暂停");
|
||||
}
|
||||
}
|
||||
//继续录音
|
||||
,recResume(){
|
||||
if(RecordApp.GetCurrentRecOrNull()){
|
||||
RecordApp.Resume();
|
||||
console.log("继续录音中...");
|
||||
}
|
||||
}
|
||||
|
||||
//停止录音
|
||||
,recStop(){
|
||||
//RecordApp.UniNativeUtsPluginCallAsync("androidNotifyService",{ close:true }) //关闭Android App后台录音保活服务
|
||||
|
||||
RecordApp.Stop((arrayBuffer,duration,mime)=>{
|
||||
//全平台通用:arrayBuffer是音频文件二进制数据,可以保存成文件或者发送给服务器
|
||||
//App中如果在Start参数中提供了stop_renderjs,renderjs中的函数会比这个函数先执行
|
||||
|
||||
//注意:当Start时提供了takeoffEncodeChunk后,你需要自行实时保存录音文件数据,因此Stop时返回的arrayBuffer的长度将为0字节
|
||||
|
||||
//如果是H5环境,也可以直接构造成Blob/File文件对象,和Recorder使用一致
|
||||
// #ifdef H5
|
||||
var blob=new Blob([arrayBuffer],{type:mime});
|
||||
console.log(blob, (window.URL||webkitURL).createObjectURL(blob));
|
||||
var file=new File([arrayBuffer],"recorder.mp3");
|
||||
//uni.uploadFile({file:file, ...}) //参考demo中的test_upload_saveFile.vue
|
||||
// #endif
|
||||
|
||||
//如果是App、小程序环境,可以直接保存到本地文件,然后调用相关网络接口上传
|
||||
// #ifdef APP || MP-WEIXIN
|
||||
RecordApp.UniSaveLocalFile("recorder.mp3",arrayBuffer,(savePath)=>{
|
||||
console.log(savePath); //app保存的文件夹为`plus.io.PUBLIC_DOWNLOADS`,小程序为 `wx.env.USER_DATA_PATH` 路径
|
||||
//uni.uploadFile({filePath:savePath, ...}) //参考demo中的test_upload_saveFile.vue
|
||||
},(errMsg)=>{ console.error(errMsg) });
|
||||
// #endif
|
||||
},(msg)=>{
|
||||
console.error("结束录音失败:"+msg);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
# 录音权限配置、需要注意的细节
|
||||
## 编译成H5时录音和权限
|
||||
编译成H5时,录音功能由Recorder H5提供,无需额外处理录音权限。
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
## 编译成微信小程序时录音和权限
|
||||
编译成微信小程序时,录音功能由小程序的`RecorderManager`提供,屏蔽了微信原有的底层细节(无录音时长限制)。
|
||||
|
||||
小程序录音需要用户授予录音权限,调用`RecordApp.RequestPermission`的时候会检查是否能正常录音,如果用户拒绝了录音权限,会进入错误回调,回调里面你应当编写代码检查`wx.getSetting`中的`scope.record`录音权限,然后引导用户进行授权(可调用`wx.openSetting`打开设置页面,方便用户给权限)。
|
||||
|
||||
**注意:上架小程序需要到小程序管理后台《[用户隐私保护指引](https://developers.weixin.qq.com/miniprogram/dev/framework/user-privacy/miniprogram-intro.html)》中声明录音权限,否则正式版将无法调用录音功能(请求权限时会直接走错误回调)。**
|
||||
|
||||
更多细节请参考 [miniProgram-wx](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/miniProgram-wx) 测试项目文档。
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
## 编译成App时录音和权限
|
||||
编译成App录音时,分两种情况:
|
||||
1. 默认未配置`RecordApp.UniNativeUtsPlugin`(未使用原生录音插件和uts插件)时,会在renderjs中使用Recorder H5进行录音,录音数据会实时回传到逻辑层。
|
||||
2. 配置了`RecordApp.UniNativeUtsPlugin`使用原生录音插件或uts插件时,会直接调用原生插件进行录音;录音数据默认会传递到renderjs中进行音频编码处理(WebWorker加速),然后再实时回传到逻辑层,如果配置了`RecordApp.UniWithoutAppRenderjs=true`时,音频编码处理将会在逻辑层中直接处理。
|
||||
|
||||
当App是在renderjs中使用H5进行录音时(未使用原生录音插件和uts插件),iOS上只支持14.3以上版本,**且iOS上每次进入页面后第一次请求录音权限时、或长时间无操作再请求录音权限时WebView均会弹出录音权限对话框**,不同旧iOS版本(低于iOS17)下H5录音可能存在的问题在App中同样会存在;使用配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin)或uts插件时无以上问题和版本限制(uts插件开发中暂不可用),Android也无以上问题。
|
||||
|
||||
当音频编码是在renderjs中进行处理时,录音结束后会将整个录音文件传回逻辑层,由于uni-app的逻辑层和renderjs层大点的文件传输会比较慢,**建议Start时使用takeoffEncodeChunk实时获取音频文件数据可避免Stop时产生超大数据回传**;配置了`RecordApp.UniWithoutAppRenderjs=true`后,因为音频编码直接是在逻辑层中进行,将不存在传输性能损耗,但会影响逻辑层的性能(正常情况轻微不明显),需要配套使用原生录音插件才可以进行此项配置。
|
||||
|
||||
在调用`RecordApp.RequestPermission`的时候,`Recorder-UniCore`组件会自动处理好App的系统录音权限,只需要在uni-app项目的 `manifest.json` 中配置好Android和iOS的录音权限声明。
|
||||
```
|
||||
//Android需要勾选的权限,第二个也必须勾选
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
【注意】Android如果需要在后台录音,需要启用后台录音保活服务,Android 9开始,锁屏或进入后台一段时间后App可能会被禁止访问麦克风导致录音静音、无法录音(renderjs中H5录音、原生插件录音均受影响),请调用配套原生插件的`androidNotifyService`接口,或使用第三方保活插件
|
||||
|
||||
//iOS需要声明的权限
|
||||
NSMicrophoneUsageDescription
|
||||
【注意】iOS需要在 `App常用其它设置`->`后台运行能力`中提供`audio`配置,不然App切到后台后立马会停止录音
|
||||
```
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
## PCM音频流式播放、语音通话、回声消除、声音外放
|
||||
在App、H5中,均可使用H5版的[BufferStreamPlayer](https://gitee.com/xiangyuecn/Recorder/blob/master/src/extensions/buffer_stream.player.js)来实时流式播放语音;其中App中需要在renderjs中加载BufferStreamPlayer,在逻辑层中调用`RecordApp.UniWebViewVueCall`等方法将逻辑层中接收到的实时语音数据发送到renderjs中播放;播放声音的同时进行录音,声音可能会被录进去产生回声,因此一般需要打开回声消除;调用代码参考demo中的[test_realtime_voice.vue](https://gitee.com/xiangyuecn/Recorder/blob/master/app-support-sample/demo_UniApp/pages/recTest/test_realtime_voice.vue)。
|
||||
|
||||
App中如果搭配使用了配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin),可以调用原生实现的PcmPlayer播放器实时流式播放PCM音频,边录音边播放更流畅;同时也支持完整播放,比如AI语音合成的播放;调用代码参考demo中的[test_player_nativePlugin_pcmPlayer.vue](https://gitee.com/xiangyuecn/Recorder/blob/master/app-support-sample/demo_UniApp/pages/recTest/test_player_nativePlugin_pcmPlayer.vue)。
|
||||
|
||||
微信小程序请参考 [miniProgram-wx](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/miniProgram-wx) 文档里面的同名章节,使用WebAudioContext播放。
|
||||
|
||||
配置audioTrackSet可尝试打开回声消除,或者切换听筒播放或外放,打开回声消除时,一般会转为听筒播放显著降低回声。
|
||||
``` js
|
||||
//打开回声消除
|
||||
RecordApp.Start({
|
||||
... 更多配置参数请参考RecordApp文档
|
||||
//此配置App、H5、小程序均可打开回声消除;注意:H5、App+renderjs中需要在请求录音权限前进行相同配置RecordApp.RequestPermission_H5OpenSet后此配置才会生效
|
||||
,audioTrackSet:{echoCancellation:true,noiseSuppression:true,autoGainControl:true}
|
||||
|
||||
//Android指定麦克风源(App搭配原生插件、小程序可用),0 DEFAULT 默认音频源,1 MIC 主麦克风,5 CAMCORDER 相机方向的麦,6 VOICE_RECOGNITION 语音识别,7 VOICE_COMMUNICATION 语音通信(带回声消除)
|
||||
,android_audioSource:7 //提供此配置时优先级比audioTrackSet更高,默认值为0
|
||||
|
||||
//iOS的AVAudioSession setCategory的withOptions参数值(App搭配原生插件可用),取值请参考配套原生插件文档中的iosSetDefault_categoryOptions
|
||||
//,ios_categoryOptions:0x1|0x4 //默认值为5(0x1|0x4)
|
||||
});
|
||||
|
||||
//App搭配原生插件时尝试切换听筒播放或外放
|
||||
await RecordApp.UniNativeUtsPluginCallAsync("setSpeakerOff",{off:true或false});
|
||||
//小程序尝试切换
|
||||
wx.setInnerAudioOption({ speakerOn:false或true })
|
||||
//H5不支持切换
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
# 详细文档、RecordApp方法、属性文档
|
||||
请先阅读 [demo_UniApp文档](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp),含Demo项目;更高级使用还需深入阅读 [Recorder文档](https://gitee.com/xiangyuecn/Recorder)、[RecordApp文档](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample) (均为完整的一个README.md文档),Recorder文档中包含了更丰富的示例代码:基础录音、实时处理、格式转码、音频分析、音频混音、音频生成 等等,大部分能在uniapp中直接使用。
|
||||
|
||||
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
# 本组件的授权许可限制
|
||||
**本组件内的app-uni-support.js文件在uni-app中编译到App平台时仅供测试用(App平台包括:Android App、iOS App),不可用于正式发布或商用,正式发布或商用需先到DCloud插件市场购买[此带授权的插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin-Android)(费用为¥199元,赠送Android版原生插件),即可获得授权许可**;编译到其他平台时无此授权限制,比如:H5、小程序,均为免费授权。
|
||||
|
||||
在App中,如果未获得授权许可,将会在App打开后第一次调用`RecordApp.RequestPermission`请求录音权限时,弹出“未获得商用授权时,App上仅供测试”提示框。
|
||||
|
||||
在DCloud插件市场购买了[带授权的插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin-Android)获得了授权后,请在调用`RecordApp.RequestPermission`请求录音权限前,赋值`RecordApp.UniAppUseLicense="我已获得UniAppID=***的商用授权"`(星号为你项目的uni-app应用标识),就不会弹提示框了;或者直接使用配套的[原生录音插件](https://ext.dcloud.net.cn/plugin?name=Recorder-NativePlugin),设置`RecordApp.UniNativeUtsPlugin`参数后,也不会弹提示框;其他情况请联系作者咨询,更多细节请参考[本组件的GitHub文档](https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp)。
|
||||
|
||||
获取授权、需要技术支持、或有不清楚的地方可以联系我们,客服联系方式:QQ 1251654593 ,或者直接联系作者QQ 753610399 (回复可能没有客服及时)。
|
||||
|
||||
插件开发维护不易,感谢支持~
|
||||
|
||||
|
||||
*⠀*
|
||||
|
||||
*⠀*
|
||||
|
||||
|
27
src/uni_modules/all-speech/changelog.md
Normal file
27
src/uni_modules/all-speech/changelog.md
Normal file
@ -0,0 +1,27 @@
|
||||
## 1.1.2(2024-07-26)
|
||||
修复第一次录音录不上
|
||||
## 1.1.1(2024-05-22)
|
||||
1.新增node后端方便测试
|
||||
2.修复H5及H5上传示例
|
||||
## 1.0.7(2024-05-22)
|
||||
更新H5上传示例,新增node后端测试 自行进行测试
|
||||
## 1.0.6(2024-03-19)
|
||||
上传示例项目
|
||||
## 1.0.5(2024-03-19)
|
||||
修复PC语音录制
|
||||
## 1.0.4(2024-03-19)
|
||||
兼容pc
|
||||
## 1.0.0(2024-03-19)
|
||||
兼容PC
|
||||
## 1.0.3(2024-03-15)
|
||||
修复安卓导入失败
|
||||
## 1.0.2(2024-03-14)
|
||||
修复小程序报错,支持vue3
|
||||
## 1.0.1(2024-03-05)
|
||||
补充说明,原作者信息。本人只是补充
|
||||
|
||||
|
||||
## 1.0.0(2024-03-05)
|
||||
### 首次发布
|
||||
1.原地址 https://ext.dcloud.net.cn/plugin?id=9595
|
||||
2.原有基础上兼容H5,如有侵权联系立刻下架
|
841
src/uni_modules/all-speech/components/all-speech/all-speech.vue
Normal file
841
src/uni_modules/all-speech/components/all-speech/all-speech.vue
Normal file
@ -0,0 +1,841 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- <customRequestPermission
|
||||
ref="requestPer"
|
||||
:permissionInfo="permissionInfo"
|
||||
></customRequestPermission> -->
|
||||
<view
|
||||
class="record-btn"
|
||||
@longpress="startRecord"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@touchend="endRecord"
|
||||
hover-class="record-btn-hover"
|
||||
hover-start-time="200"
|
||||
hover-stay-time="150"
|
||||
:style="[
|
||||
btnStyle,
|
||||
{
|
||||
'--btn-hover-fontcolor': btnHoverFontcolor,
|
||||
'--btn-hover-bgcolor': btnHoverBgcolor,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<text>{{ btnTextContent }}</text>
|
||||
</view>
|
||||
<!-- <view class="record-popup"
|
||||
:style="{ '--popup-height': popupHeight, '--popup-width': upx2px(popupMaxWidth), '--popup-bottom': upx2px(popupFixBottom), '--popup-bg-color': popupBgColor }">
|
||||
<view class="inner-content" v-if="recordPopupShow">
|
||||
<view class="title">{{ popupTitle }}</view>
|
||||
<view class="voice-line-wrap" v-if="recording"
|
||||
:style="{ '--line-height': upx2px(lineHeight), '--line-start-color': lineStartColor, '--line-end-color': lineEndColor }">
|
||||
<view class="voice-line one"></view>
|
||||
<view class="voice-line two"></view>
|
||||
<view class="voice-line three"></view>
|
||||
<view class="voice-line four"></view>
|
||||
<view class="voice-line five"></view>
|
||||
<view class="voice-line six"></view>
|
||||
<view class="voice-line seven"></view>
|
||||
<view class="voice-line six"></view>
|
||||
<view class="voice-line five"></view>
|
||||
<view class="voice-line four"></view>
|
||||
<view class="voice-line three"></view>
|
||||
<view class="voice-line two"></view>
|
||||
<view class="voice-line one"></view>
|
||||
</view>
|
||||
<view class="cancel-icon" v-if="!recording">+</view>
|
||||
<view class="tips">{{ recording ? popupDefaultTips : popupCancelTips }}</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<view class="record-shadow" v-if="recordPopupShow">
|
||||
<view
|
||||
class="record-animation-area"
|
||||
:style="
|
||||
recording
|
||||
? ''
|
||||
: 'background-image: url(' + recordAnimationAreaCancelBg + ')'
|
||||
"
|
||||
>
|
||||
<view
|
||||
class="voice-line-wrap"
|
||||
v-if="!nearTimeLimit"
|
||||
:style="{
|
||||
'--line-height': upx2px(lineHeight),
|
||||
'--line-start-color': lineStartColor,
|
||||
'--line-end-color': lineEndColor,
|
||||
}"
|
||||
>
|
||||
<view class="voice-line seven"></view>
|
||||
<view class="voice-line six"></view>
|
||||
<view class="voice-line five"></view>
|
||||
<view class="voice-line four"></view>
|
||||
<view class="voice-line three"></view>
|
||||
<view class="voice-line two"></view>
|
||||
<view class="voice-line one"></view>
|
||||
<view class="voice-line two"></view>
|
||||
<view class="voice-line three"></view>
|
||||
<view class="voice-line four"></view>
|
||||
<view class="voice-line five"></view>
|
||||
<view class="voice-line six"></view>
|
||||
<view class="voice-line seven"></view>
|
||||
<view class="voice-line six"></view>
|
||||
<view class="voice-line five"></view>
|
||||
<view class="voice-line four"></view>
|
||||
<view class="voice-line three"></view>
|
||||
<view class="voice-line two"></view>
|
||||
<view class="voice-line one"></view>
|
||||
<view class="voice-line two"></view>
|
||||
<view class="voice-line three"></view>
|
||||
<view class="voice-line four"></view>
|
||||
<view class="voice-line five"></view>
|
||||
<view class="voice-line six"></view>
|
||||
<view class="voice-line seven"></view>
|
||||
</view>
|
||||
<view class="record-auto-stop-hint" v-if="nearTimeLimit">
|
||||
<text>{{ autoStopRecordCountDown + $t('auto_stop_record') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="record-cancel-btn">
|
||||
<image
|
||||
v-if="recording"
|
||||
src="@/static/image/record/chat-voice-cancel-grey.png"
|
||||
></image>
|
||||
<image
|
||||
v-if="!recording"
|
||||
src="@/static/image/record/chat-voice-cancel-blue.png"
|
||||
></image>
|
||||
<text v-if="recording" class="font-36 font-regular"></text>
|
||||
<text v-if="!recording" class="font-36 font-regular" style="margin:20rpx 0 0;">
|
||||
{{ $t('release_hand_to_cancel') }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
class="record-shadow-bottom"
|
||||
:style="recording ? '' : 'background-color:#fff;'"
|
||||
>
|
||||
<image
|
||||
v-if="recording"
|
||||
src="@/static/image/record/chat-voice-icon-white.png"
|
||||
></image>
|
||||
<image
|
||||
v-if="!recording"
|
||||
src="@/static/image/record/chat-voice-icon-blue.png"
|
||||
></image>
|
||||
<text class="font-36 font-regular">
|
||||
{{ $t('release_hand_to_send') }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
var that
|
||||
// #ifdef APP-PLUS
|
||||
const recorderManager = uni.getRecorderManager()
|
||||
// 引入权限判断
|
||||
import permision from '../../js_sdk/wa-permission/permission.js'
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
import Recorder from 'recorder-core'
|
||||
import RecordApp from 'recorder-core/src/app-support/app'
|
||||
import '@/uni_modules/Recorder-UniCore/app-uni-support.js'
|
||||
import 'recorder-core/src/engine/mp3'
|
||||
import 'recorder-core/src/engine/mp3-engine'
|
||||
import 'recorder-core/src/extensions/waveview'
|
||||
import recordCancelBg from '@/static/image/record/chat-voice-animation-bg-red.png'
|
||||
// #endif
|
||||
import { multiplication } from '@/utils/common'
|
||||
export default {
|
||||
name: 'nbVoiceRecord',
|
||||
/**
|
||||
* 录音交互动效组件
|
||||
* @property {Object} recordOptions 录音配置
|
||||
* @property {Object} btnStyle 按钮样式
|
||||
* @property {Object} btnHoverFontcolor 按钮长按时字体颜色
|
||||
* @property {String} btnHoverBgcolor 按钮长按时背景颜色
|
||||
* @property {String} btnDefaultText 按钮初始文字
|
||||
* @property {String} btnRecordingText 录制时按钮文字
|
||||
* @property {Boolean} vibrate 弹窗时是否震动
|
||||
* @property {String} popupTitle 弹窗顶部文字
|
||||
* @property {String} popupDefaultTips 录制时弹窗底部提示
|
||||
* @property {String} popupCancelTips 滑动取消时弹窗底部提示
|
||||
* @property {String} popupMaxWidth 弹窗展开后宽度
|
||||
* @property {String} popupMaxHeight 弹窗展开后高度
|
||||
* @property {String} popupFixBottom 弹窗展开后距底部高度
|
||||
* @property {String} popupBgColor 弹窗背景颜色
|
||||
* @property {String} lineHeight 声波高度
|
||||
* @property {String} lineStartColor 声波波谷时颜色色值
|
||||
* @property {String} lineEndColor 声波波峰时颜色色值
|
||||
* @event {Function} startRecord 开始录音回调
|
||||
* @event {Function} endRecord 结束录音回调
|
||||
* @event {Function} cancelRecord 滑动取消录音回调
|
||||
* @event {Function} stopRecord 主动停止录音
|
||||
*/
|
||||
props: {
|
||||
recordOptions: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
duration: 60000,
|
||||
} // 请自行查看各端的的支持情况,这里全部使用默认配置
|
||||
},
|
||||
},
|
||||
chatInputHeight: {
|
||||
type: String,
|
||||
default: '42px', // 单位为px
|
||||
},
|
||||
btnStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
width: 'calc(100% - 10rpx)',
|
||||
height: '42px',
|
||||
borderRadius: '8rpx',
|
||||
backgroundColor: '#F9F9F9',
|
||||
border: '1rpx solid whitesmoke',
|
||||
permisionState: false,
|
||||
margin: '0 10rpx 0 0',
|
||||
// boxShadow: '0rpx 6rpx 12rpx 2rpx rgba(0, 0, 0, 0.08)',
|
||||
}
|
||||
},
|
||||
},
|
||||
btnHoverFontcolor: {
|
||||
type: String,
|
||||
default: '#000', // 颜色名称或16进制色值
|
||||
},
|
||||
btnHoverBgcolor: {
|
||||
type: String,
|
||||
default: 'whitesmoke', // 颜色名称或16进制色值
|
||||
},
|
||||
btnDefaultText: {
|
||||
type: String,
|
||||
default: '长按开始录音',
|
||||
},
|
||||
btnRecordingText: {
|
||||
type: String,
|
||||
default: '录音中',
|
||||
},
|
||||
vibrate: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
popupTitle: {
|
||||
type: String,
|
||||
default: '正在录制音频',
|
||||
},
|
||||
popupDefaultTips: {
|
||||
type: String,
|
||||
default: '左右滑动后松手完成录音',
|
||||
},
|
||||
popupCancelTips: {
|
||||
type: String,
|
||||
default: '松手取消录音',
|
||||
},
|
||||
popupMaxWidth: {
|
||||
type: Number,
|
||||
default: 600, // 单位为rpx
|
||||
},
|
||||
popupMaxHeight: {
|
||||
type: Number,
|
||||
default: 300, // 单位为rpx
|
||||
},
|
||||
popupFixBottom: {
|
||||
type: Number,
|
||||
default: 200, // 单位为rpx
|
||||
},
|
||||
popupBgColor: {
|
||||
type: String,
|
||||
default: 'whitesmoke',
|
||||
},
|
||||
lineHeight: {
|
||||
type: Number,
|
||||
default: 50, // 单位为rpx
|
||||
},
|
||||
lineStartColor: {
|
||||
type: String,
|
||||
default: 'royalblue', // 颜色名称或16进制色值
|
||||
},
|
||||
lineEndColor: {
|
||||
type: String,
|
||||
default: 'indianred', // 颜色名称或16进制色值
|
||||
},
|
||||
field: {
|
||||
type: String,
|
||||
default: 'voiceRecord',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stopStatus: false, // 是否已被父页面通知主动结束录音
|
||||
btnTextContent: this.btnDefaultText,
|
||||
startTouchData: {},
|
||||
popupHeight: '0px', // 这是初始的高度
|
||||
recording: true, // 录音中
|
||||
recordPopupShow: false,//是否显示录音弹窗
|
||||
recordTimeout: null, // 录音定时器
|
||||
recordAnimationAreaCancelBg: recordCancelBg, //录音动画区域取消时背景
|
||||
permissionInfo: '', //当前请求的权限
|
||||
nearTimeLimit: false, //是否录音将超时
|
||||
autoStopRecordCountDown: 0, //自动停止录音倒计时
|
||||
autoStopRecordInterval: null, //自动停止录音技术定时器
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let that = this
|
||||
that.isMounted = true
|
||||
RecordApp.UniRenderjsRegister(that)
|
||||
window.handleAppMicro = that.handleAppMicro
|
||||
// that.$store.dispatch('updateCurrentComponentRef', that)
|
||||
},
|
||||
onShow() {
|
||||
let that = this
|
||||
if (that.isMounted) RecordApp.UniPageOnShow(that)
|
||||
},
|
||||
created() {
|
||||
let that = this
|
||||
|
||||
// 请求权限
|
||||
that.checkPermission()
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
recorderManager.onStop((res) => {
|
||||
// 判断是否用户主动结束录音
|
||||
if (that.recordTimeout !== null) {
|
||||
// 延时未结束,则是主动结束录音
|
||||
clearTimeout(that.recordTimeout)
|
||||
that.recordTimeout = null // 恢复状态
|
||||
}
|
||||
|
||||
// 继续判断是否为取消录音
|
||||
if (that.recording) {
|
||||
that.$emit('endRecord', res)
|
||||
} else {
|
||||
// 用户向上滑动,此时松手后响应的是取消录音的回调
|
||||
that.recording = true // 恢复状态
|
||||
that.$emit('cancelRecord')
|
||||
}
|
||||
if (that.autoStopRecordInterval) {
|
||||
clearInterval(that.autoStopRecordInterval)
|
||||
that.autoStopRecordInterval = null
|
||||
}
|
||||
})
|
||||
|
||||
recorderManager.onError((err) => {
|
||||
console.log('err:', err)
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
upx2px(upx) {
|
||||
return uni.upx2px(upx) + 'px'
|
||||
},
|
||||
handleAppMicro() {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.requestPer.hideCustomPermission()
|
||||
}, 1000)
|
||||
that.continueAppMicro()
|
||||
},
|
||||
handleRequestPer(permissionInfo, permissionID) {
|
||||
console.log(permissionInfo)
|
||||
let that = this
|
||||
if (permissionInfo == 'closePermissionRequest') {
|
||||
that.permissionInfo = ''
|
||||
that.$refs.requestPer.hideCustomPermission()
|
||||
} else if (permissionInfo == 'showPermissionRefuseModal') {
|
||||
that.$refs.requestPer.showupCustomPermission()
|
||||
that.permissionInfo = permissionID
|
||||
console.log('权限被拒绝:' + permissionID)
|
||||
that.$refs.requestPer.showupPopupPrompt()
|
||||
} else {
|
||||
that.$refs.requestPer.showupCustomPermission()
|
||||
that.permissionInfo = permissionInfo
|
||||
}
|
||||
},
|
||||
continueAppMicro() {
|
||||
let that = this
|
||||
RecordApp.UniWebViewActivate(that) //App环境下必须先切换成当前页面WebView
|
||||
RecordApp.RequestPermission(
|
||||
() => {
|
||||
console.log('已获得录音权限,可以开始录音了')
|
||||
that.permisionState = true
|
||||
},
|
||||
(msg, isUserNotAllow) => {
|
||||
if (isUserNotAllow) {
|
||||
//用户拒绝了录音权限
|
||||
//这里你应当编写代码进行引导用户给录音权限,不同平台分别进行编写
|
||||
}
|
||||
console.error('请求录音权限失败:' + msg)
|
||||
},
|
||||
)
|
||||
},
|
||||
async checkPermission() {
|
||||
let that = this
|
||||
// #ifdef APP-PLUS
|
||||
// 先判断os
|
||||
let os = uni.getSystemInfoSync().osName
|
||||
if (os == 'ios') {
|
||||
that.permisionState = await permision.judgeIosPermission('record')
|
||||
} else {
|
||||
that.permisionState = await permision.requestAndroidPermission(
|
||||
'android.permission.RECORD_AUDIO',
|
||||
)
|
||||
}
|
||||
if (that.permisionState !== true && that.permisionState !== 1) {
|
||||
uni.showToast({
|
||||
title: '请先授权使用录音',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS
|
||||
// #ifndef H5
|
||||
uni.authorize({
|
||||
scope: 'scope.record',
|
||||
success: () => {
|
||||
that.permisionState = true
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '请授权使用录音',
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if (typeof plus !== 'undefined') {
|
||||
webUni.postMessage({
|
||||
data: {
|
||||
action: 'handleRequestAndroidPermission',
|
||||
params: {
|
||||
type: 'micro',
|
||||
key: that.field,
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
that.continueAppMicro()
|
||||
}
|
||||
// #endif
|
||||
// #endif
|
||||
},
|
||||
|
||||
startRecord() {
|
||||
let that = this
|
||||
if (!that.permisionState) {
|
||||
that.checkPermission()
|
||||
return
|
||||
}
|
||||
that.stopStatus = false
|
||||
setTimeout(() => {
|
||||
that.popupHeight = that.upx2px(that.popupMaxHeight)
|
||||
setTimeout(() => {
|
||||
that.recordPopupShow = true
|
||||
that.btnTextContent = that.btnRecordingText
|
||||
if (that.vibrate) {
|
||||
// #ifdef APP-PLUS
|
||||
plus.device.vibrate(35)
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.vibrateShort()
|
||||
// #endif
|
||||
}
|
||||
// 开始录音
|
||||
// #ifdef APP-PLUS
|
||||
recorderManager.start(that.recordOptions)
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
// #ifdef H5
|
||||
//录音配置信息
|
||||
var set = {
|
||||
type: 'mp3',
|
||||
sampleRate: 16000,
|
||||
bitRate: 16,
|
||||
onProcess: (
|
||||
buffers,
|
||||
powerLevel,
|
||||
duration,
|
||||
sampleRate,
|
||||
newBufferIdx,
|
||||
asyncEnd,
|
||||
) => {
|
||||
// #ifdef H5 || MP-WEIXIN
|
||||
if (that.waveView)
|
||||
that.waveView.input(
|
||||
buffers[buffers.length - 1],
|
||||
powerLevel,
|
||||
sampleRate,
|
||||
)
|
||||
// #endif
|
||||
if (that.clearBufferIdx > newBufferIdx) {
|
||||
that.clearBufferIdx = 0
|
||||
} //重新录音了就重置
|
||||
for (var i = that.clearBufferIdx || 0; i < newBufferIdx; i++)
|
||||
buffers[i] = null
|
||||
that.clearBufferIdx = newBufferIdx
|
||||
},
|
||||
}
|
||||
RecordApp.UniWebViewActivate(that) //App环境下必须先切换成当前页面WebView
|
||||
RecordApp.Start(
|
||||
set,
|
||||
() => {
|
||||
console.log('已开始录音')
|
||||
},
|
||||
(msg) => {
|
||||
console.error('开始录音失败:' + msg)
|
||||
},
|
||||
)
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
that.nearTimeLimit = false
|
||||
// 设置定时器
|
||||
that.recordTimeout = setTimeout(
|
||||
() => {
|
||||
// 如果定时器先结束,则说明此时录音时间超限
|
||||
that.stopRecord() // 结束录音动画(实际上录音的end回调已经先执行)
|
||||
that.recordTimeout = null // 重置
|
||||
},
|
||||
that.recordOptions.duration ? that.recordOptions.duration : 600000,
|
||||
)
|
||||
|
||||
that.autoStopRecordCountDown = 11
|
||||
let count = 0
|
||||
that.autoStopRecordInterval = setInterval(() => {
|
||||
count += 1000
|
||||
if (
|
||||
count >=
|
||||
that.recordOptions.duration -
|
||||
multiplication(that.autoStopRecordCountDown, 1000)
|
||||
) {
|
||||
that.nearTimeLimit = true
|
||||
that.autoStopRecordCountDown--
|
||||
if (that.autoStopRecordCountDown <= 0) {
|
||||
clearInterval(that.autoStopRecordInterval)
|
||||
that.autoStopRecordInterval = null
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
that.$emit('startRecord')
|
||||
}, 100)
|
||||
}, 200)
|
||||
},
|
||||
endRecord() {
|
||||
let that = this
|
||||
if (that.stopStatus) {
|
||||
return
|
||||
}
|
||||
that.popupHeight = '0px'
|
||||
that.recordPopupShow = false
|
||||
that.btnTextContent = that.btnDefaultText
|
||||
// #ifdef APP-PLUS
|
||||
recorderManager.stop()
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
// #ifdef H5
|
||||
RecordApp.Stop(
|
||||
(arrayBuffer, duration, mime) => {
|
||||
var blob = new Blob([arrayBuffer], {
|
||||
type: mime,
|
||||
})
|
||||
let url = (window.URL || webkitURL).createObjectURL(blob)
|
||||
console.log(blob, url)
|
||||
var file = new File([arrayBuffer], 'recorder.mp3')
|
||||
// 判断是否用户主动结束录音
|
||||
if (that.recordTimeout !== null) {
|
||||
// 延时未结束,则是主动结束录音
|
||||
clearTimeout(that.recordTimeout)
|
||||
that.recordTimeout = null // 恢复状态
|
||||
}
|
||||
// 继续判断是否为取消录音
|
||||
if (that.recording) {
|
||||
// console.log(file)
|
||||
that.$emit('endRecord', file, url, duration)
|
||||
;(window.URL || webkitURL).revokeObjectURL(url)
|
||||
} else {
|
||||
// 用户向上滑动,此时松手后响应的是取消录音的回调
|
||||
that.recording = true // 恢复状态
|
||||
that.$emit('cancelRecord')
|
||||
}
|
||||
if (that.autoStopRecordInterval) {
|
||||
clearInterval(that.autoStopRecordInterval)
|
||||
that.autoStopRecordInterval = null
|
||||
}
|
||||
},
|
||||
(msg) => {
|
||||
console.error('结束录音失败:' + msg)
|
||||
},
|
||||
)
|
||||
// #endif
|
||||
// #endif
|
||||
},
|
||||
stopRecord() {
|
||||
let that = this
|
||||
// 用法如你录音限制了时间,那么将在结束时强制停止组件的显示
|
||||
that.endRecord()
|
||||
that.stopStatus = true
|
||||
},
|
||||
touchStart(e) {
|
||||
let that = this
|
||||
that.startTouchData.clientX = e.changedTouches[0].clientX //手指按下时的X坐标
|
||||
that.startTouchData.clientY = e.changedTouches[0].clientY //手指按下时的Y坐标
|
||||
},
|
||||
touchMove(e) {
|
||||
let that = this
|
||||
let touchData = e.touches[0] //滑动过程中,手指滑动的坐标信息 返回的是Objcet对象
|
||||
let moveX = touchData.clientX - that.startTouchData.clientX
|
||||
let moveY = touchData.clientY - that.startTouchData.clientY
|
||||
if (moveY < -130) {
|
||||
if (that.vibrate && that.recording) {
|
||||
// #ifdef APP-PLUS
|
||||
plus.device.vibrate(35)
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.vibrateShort()
|
||||
// #endif
|
||||
}
|
||||
that.recording = false
|
||||
} else {
|
||||
that.recording = true
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.record-btn {
|
||||
color: #000;
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: 0.25s all;
|
||||
border: 1rpx solid whitesmoke;
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
color: #1a1a1a;
|
||||
box-sizing: border-box;
|
||||
text{
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.record-btn-hover {
|
||||
color: var(--btn-hover-fontcolor) !important;
|
||||
background-color: var(--btn-hover-bgcolor) !important;
|
||||
}
|
||||
|
||||
.record-popup {
|
||||
position: absolute;
|
||||
bottom: var(--popup-bottom);
|
||||
left: calc(50vw - calc(var(--popup-width) / 2));
|
||||
z-index: 1;
|
||||
width: var(--popup-width);
|
||||
height: var(--popup-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||
background: var(--popup-bg-color);
|
||||
color: #000;
|
||||
transition: 0.2s height;
|
||||
|
||||
.inner-content {
|
||||
height: var(--popup-height);
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #999;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
line-height: 44rpx;
|
||||
background-color: pink;
|
||||
border-radius: 50%;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.voice-line-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.voice-line {
|
||||
width: 5rpx;
|
||||
height: var(--line-height);
|
||||
border-radius: 3rpx;
|
||||
margin: 0 5rpx;
|
||||
}
|
||||
|
||||
.one {
|
||||
animation: wave 0.4s 1s linear infinite alternate;
|
||||
}
|
||||
|
||||
.two {
|
||||
animation: wave 0.4s 0.9s linear infinite alternate;
|
||||
}
|
||||
|
||||
.three {
|
||||
animation: wave 0.4s 0.8s linear infinite alternate;
|
||||
}
|
||||
|
||||
.four {
|
||||
animation: wave 0.4s 0.7s linear infinite alternate;
|
||||
}
|
||||
|
||||
.five {
|
||||
animation: wave 0.4s 0.6s linear infinite alternate;
|
||||
}
|
||||
|
||||
.six {
|
||||
animation: wave 0.4s 0.5s linear infinite alternate;
|
||||
}
|
||||
|
||||
.seven {
|
||||
animation: wave 0.4s linear infinite alternate;
|
||||
}
|
||||
}
|
||||
|
||||
.record-auto-stop-hint {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text {
|
||||
color: #fff;
|
||||
margin-top: -24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wave {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
background-color: var(--line-start-color);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1, 0.2);
|
||||
background-color: var(--line-end-color);
|
||||
}
|
||||
}
|
||||
|
||||
.record-shadow {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
z-index: 12;
|
||||
|
||||
.record-animation-area {
|
||||
position: absolute;
|
||||
bottom: 50%;
|
||||
left: 50%;
|
||||
width: 468rpx;
|
||||
height: 143rpx;
|
||||
margin-top: -71.5rpx;
|
||||
margin-left: -234rpx;
|
||||
background-image: url('@/static/image/record/chat-voice-animation-bg-blue.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.voice-line-wrap {
|
||||
margin-top: -17rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.record-cancel-btn {
|
||||
position: absolute;
|
||||
bottom: 320rpx;
|
||||
left: 50%;
|
||||
margin-left: -74rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
image {
|
||||
width: 148rpx;
|
||||
height: 148rpx;
|
||||
}
|
||||
|
||||
text {
|
||||
color: #fff;
|
||||
margin: 8rpx;
|
||||
height: 48rpx;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.record-shadow-bottom {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 274rpx;
|
||||
background-color: #4686e2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
image {
|
||||
width: 44rpx;
|
||||
height: 62rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
text {
|
||||
margin: 22rpx 0 0;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.record-shadow-bottom::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 68rpx;
|
||||
border-radius: 100%;
|
||||
background: inherit;
|
||||
bottom: 274rpx;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 50%);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
226
src/uni_modules/all-speech/js_sdk/h5-speech/speech.js
Normal file
226
src/uni_modules/all-speech/js_sdk/h5-speech/speech.js
Normal file
@ -0,0 +1,226 @@
|
||||
class Recoder {
|
||||
constructor(sampleRate) {
|
||||
this.leftDataList = []
|
||||
this.rightDataList = []
|
||||
this.mediaPlayer = null
|
||||
this.audioContext = null
|
||||
this.source = null
|
||||
this.sampleRate = sampleRate || 44100
|
||||
}
|
||||
start() {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
sampleRate: 8000, // 采样率
|
||||
channelCount: 1, // 声道
|
||||
audioBitsPerSecond: 64,
|
||||
volume: 1.0, // 音量
|
||||
autoGainControl: true
|
||||
}
|
||||
}).then(mediaStream => {
|
||||
console.log(mediaStream, 'mediaStream')
|
||||
this.mediaPlayer = mediaStream
|
||||
this.beginRecord(mediaStream)
|
||||
resolve()
|
||||
}).catch(err => {
|
||||
// 如果用户电脑没有麦克风设备或者用户拒绝了,或者连接出问题了等
|
||||
// 这里都会抛异常,并且通过err.name可以知道是哪种类型的错误
|
||||
console.error(err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
beginRecord(mediaStream) {
|
||||
let audioContext = new(window.AudioContext || window.webkitAudioContext)()
|
||||
// mediaNode包含 mediaStream,audioContext
|
||||
let mediaNode = audioContext.createMediaStreamSource(mediaStream)
|
||||
console.log(mediaNode, 'mediaNode')
|
||||
// 创建一个jsNode
|
||||
// audioContext.sampleRate = 8000
|
||||
console.log(audioContext, 'audioContext')
|
||||
let jsNode = this.createJSNode(audioContext)
|
||||
console.log(jsNode, 'jsnode')
|
||||
// 需要连到扬声器消费掉outputBuffer,process回调才能触发
|
||||
// 并且由于不给outputBuffer设置内容,所以扬声器不会播放出声音
|
||||
jsNode.connect(audioContext.destination)
|
||||
jsNode.onaudioprocess = this.onAudioProcess.bind(this)
|
||||
// 把mediaNode连接到jsNode
|
||||
mediaNode.connect(jsNode)
|
||||
this.audioContext = audioContext
|
||||
}
|
||||
|
||||
onAudioProcess(event) {
|
||||
console.log('is recording')
|
||||
// 拿到输入buffer Float32Array
|
||||
let audioBuffer = event.inputBuffer
|
||||
let leftChannelData = audioBuffer.getChannelData(0)
|
||||
// let rightChannelData = audioBuffer.getChannelData(1)
|
||||
|
||||
// 需要克隆一下
|
||||
this.leftDataList.push(leftChannelData.slice(0))
|
||||
//this.rightDataList.push(rightChannelData.slice(0))
|
||||
}
|
||||
|
||||
createJSNode(audioContext) {
|
||||
const BUFFER_SIZE = 4096
|
||||
const INPUT_CHANNEL_COUNT = 1
|
||||
const OUTPUT_CHANNEL_COUNT = 1
|
||||
// createJavaScriptNode已被废弃
|
||||
let creator = audioContext.createScriptProcessor || audioContext.createJavaScriptNode
|
||||
creator = creator.bind(audioContext)
|
||||
return creator(BUFFER_SIZE, INPUT_CHANNEL_COUNT, OUTPUT_CHANNEL_COUNT)
|
||||
}
|
||||
|
||||
playRecord(arrayBuffer) {
|
||||
let blob = new Blob([new Int8Array(arrayBuffer)], {
|
||||
type: 'audio/mp3' // files[0].type
|
||||
})
|
||||
let blobUrl = URL.createObjectURL(blob)
|
||||
this.source = blob
|
||||
this.blobUrl = blobUrl
|
||||
// document.querySelector(‘.audio-node‘).src = blobUrl
|
||||
return blobUrl
|
||||
}
|
||||
|
||||
stop() {
|
||||
// 停止录音
|
||||
let leftData = this.mergeArray(this.leftDataList)
|
||||
//let rightData = this.mergeArray(this.rightDataList)
|
||||
let allData = this.interSingleData(leftData)
|
||||
let wavBuffer = this.createWavFile(allData)
|
||||
|
||||
// 转到播放
|
||||
let source = this.playRecord(wavBuffer)
|
||||
// if (source) {
|
||||
// source = source.slice(5)
|
||||
// }
|
||||
console.log("我最后转换完播放的---------------------", source);
|
||||
this.resetRecord()
|
||||
return source
|
||||
}
|
||||
|
||||
transformArrayBufferToBase64(buffer) {
|
||||
var binary = ''
|
||||
var bytes = new Uint8Array(buffer)
|
||||
for (var len = bytes.byteLength, i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i])
|
||||
}
|
||||
return window.btoa(binary)
|
||||
}
|
||||
|
||||
// 停止控件录音
|
||||
resetRecord() {
|
||||
this.leftDataList = []
|
||||
this.rightDataList = []
|
||||
this.audioContext.close()
|
||||
this.mediaPlayer.getAudioTracks().forEach(track => {
|
||||
track.stop()
|
||||
this.mediaPlayer.removeTrack(track)
|
||||
})
|
||||
}
|
||||
|
||||
createWavFile(audioData) {
|
||||
let channelCount = 1
|
||||
const WAV_HEAD_SIZE = 44
|
||||
const sampleBits = 16
|
||||
let sampleRate = this.sampleRate
|
||||
|
||||
let buffer = new ArrayBuffer(audioData.length * 2 + WAV_HEAD_SIZE)
|
||||
// 需要用一个view来操控buffer
|
||||
let view = new DataView(buffer)
|
||||
// 写入wav头部信息
|
||||
// RIFF chunk descriptor/identifier
|
||||
this.writeUTFBytes(view, 0, 'RIFF')
|
||||
// RIFF chunk length
|
||||
view.setUint32(4, 44 + audioData.length * channelCount, true)
|
||||
// RIFF type
|
||||
this.writeUTFBytes(view, 8, 'WAVE')
|
||||
// format chunk identifier
|
||||
// FMT sub-chunk
|
||||
this.writeUTFBytes(view, 12, 'fmt ')
|
||||
// format chunk length
|
||||
view.setUint32(16, 16, true)
|
||||
// sample format (raw)
|
||||
view.setUint16(20, 1, true)
|
||||
// stereo (2 channels)
|
||||
view.setUint16(22, channelCount, true)
|
||||
// sample rate
|
||||
view.setUint32(24, sampleRate, true)
|
||||
// byte rate (sample rate * block align)
|
||||
view.setUint32(28, sampleRate * 2, true)
|
||||
// block align (channel count * bytes per sample)
|
||||
view.setUint16(32, 2 * 2, true)
|
||||
// bits per sample
|
||||
view.setUint16(34, 16, true)
|
||||
// data sub-chunk
|
||||
// data chunk identifier
|
||||
this.writeUTFBytes(view, 36, 'data')
|
||||
// data chunk length
|
||||
view.setUint32(40, audioData.length * 2, true)
|
||||
|
||||
console.log(view, 'view')
|
||||
let length = audioData.length
|
||||
let index = 44
|
||||
let volume = 1
|
||||
for (let i = 0; i < length; i++) {
|
||||
view.setInt16(index, audioData[i] * (0x7FFF * volume), true)
|
||||
index += 2
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
writeUTFBytes(view, offset, string) {
|
||||
var lng = string.length
|
||||
for (var i = 0; i < lng; i++) {
|
||||
view.setUint8(offset + i, string.charCodeAt(i))
|
||||
}
|
||||
}
|
||||
|
||||
interSingleData(left) {
|
||||
var t = left.length;
|
||||
let sampleRate = this.audioContext.sampleRate,
|
||||
outputSampleRate = this.sampleRate
|
||||
sampleRate += 0.0;
|
||||
outputSampleRate += 0.0;
|
||||
var s = 0,
|
||||
o = sampleRate / outputSampleRate,
|
||||
u = Math.ceil(t * outputSampleRate / sampleRate),
|
||||
a = new Float32Array(u);
|
||||
for (let i = 0; i < u; i++) {
|
||||
a[i] = left[Math.floor(s)];
|
||||
s += o;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// 交叉合并左右声道的数据
|
||||
interleaveLeftAndRight(left, right) {
|
||||
let totalLength = left.length + right.length
|
||||
let data = new Float32Array(totalLength)
|
||||
for (let i = 0; i < left.length; i++) {
|
||||
let k = i * 2
|
||||
data[k] = left[i]
|
||||
data[k + 1] = right[i]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
mergeArray(list) {
|
||||
let length = list.length * list[0].length
|
||||
let data = new Float32Array(length)
|
||||
let offset = 0
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
data.set(list[i], offset)
|
||||
offset += list[i].length
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Recoder
|
272
src/uni_modules/all-speech/js_sdk/wa-permission/permission.js
Normal file
272
src/uni_modules/all-speech/js_sdk/wa-permission/permission.js
Normal file
@ -0,0 +1,272 @@
|
||||
/**
|
||||
* 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
|
||||
*/
|
||||
|
||||
var isIos
|
||||
// #ifdef APP-PLUS
|
||||
isIos = (plus.os.name == "iOS")
|
||||
// #endif
|
||||
|
||||
// 判断推送权限是否开启
|
||||
function judgeIosPermissionPush() {
|
||||
var result = false;
|
||||
var UIApplication = plus.ios.import("UIApplication");
|
||||
var app = UIApplication.sharedApplication();
|
||||
var enabledTypes = 0;
|
||||
if (app.currentUserNotificationSettings) {
|
||||
var settings = app.currentUserNotificationSettings();
|
||||
enabledTypes = settings.plusGetAttribute("types");
|
||||
console.log("enabledTypes1:" + enabledTypes);
|
||||
if (enabledTypes == 0) {
|
||||
console.log("推送权限没有开启");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("已经开启推送功能!")
|
||||
}
|
||||
plus.ios.deleteObject(settings);
|
||||
} else {
|
||||
enabledTypes = app.enabledRemoteNotificationTypes();
|
||||
if (enabledTypes == 0) {
|
||||
console.log("推送权限没有开启!");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("已经开启推送功能!")
|
||||
}
|
||||
console.log("enabledTypes2:" + enabledTypes);
|
||||
}
|
||||
plus.ios.deleteObject(app);
|
||||
plus.ios.deleteObject(UIApplication);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断定位权限是否开启
|
||||
function judgeIosPermissionLocation() {
|
||||
var result = false;
|
||||
var cllocationManger = plus.ios.import("CLLocationManager");
|
||||
var status = cllocationManger.authorizationStatus();
|
||||
result = (status != 2)
|
||||
console.log("定位权限开启:" + result);
|
||||
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
|
||||
/* var enable = cllocationManger.locationServicesEnabled();
|
||||
var status = cllocationManger.authorizationStatus();
|
||||
console.log("enable:" + enable);
|
||||
console.log("status:" + status);
|
||||
if (enable && status != 2) {
|
||||
result = true;
|
||||
console.log("手机定位服务已开启且已授予定位权限");
|
||||
} else {
|
||||
console.log("手机系统的定位没有打开或未给予定位权限");
|
||||
} */
|
||||
plus.ios.deleteObject(cllocationManger);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断麦克风权限是否开启
|
||||
function judgeIosPermissionRecord() {
|
||||
var result = false;
|
||||
var avaudiosession = plus.ios.import("AVAudioSession");
|
||||
var avaudio = avaudiosession.sharedInstance();
|
||||
var permissionStatus = avaudio.recordPermission();
|
||||
console.log("permissionStatus:" + permissionStatus);
|
||||
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
|
||||
console.log("麦克风权限没有开启");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("麦克风权限已经开启");
|
||||
}
|
||||
plus.ios.deleteObject(avaudiosession);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断相机权限是否开启
|
||||
function judgeIosPermissionCamera() {
|
||||
var result = false;
|
||||
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
|
||||
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
|
||||
console.log("authStatus:" + authStatus);
|
||||
if (authStatus == 3) {
|
||||
result = true;
|
||||
console.log("相机权限已经开启");
|
||||
} else {
|
||||
console.log("相机权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(AVCaptureDevice);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断相册权限是否开启
|
||||
function judgeIosPermissionPhotoLibrary() {
|
||||
var result = false;
|
||||
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
|
||||
var authStatus = PHPhotoLibrary.authorizationStatus();
|
||||
console.log("authStatus:" + authStatus);
|
||||
if (authStatus == 3) {
|
||||
result = true;
|
||||
console.log("相册权限已经开启");
|
||||
} else {
|
||||
console.log("相册权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(PHPhotoLibrary);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断通讯录权限是否开启
|
||||
function judgeIosPermissionContact() {
|
||||
var result = false;
|
||||
var CNContactStore = plus.ios.import("CNContactStore");
|
||||
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
|
||||
if (cnAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("通讯录权限已经开启");
|
||||
} else {
|
||||
console.log("通讯录权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(CNContactStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断日历权限是否开启
|
||||
function judgeIosPermissionCalendar() {
|
||||
var result = false;
|
||||
var EKEventStore = plus.ios.import("EKEventStore");
|
||||
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
|
||||
if (ekAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("日历权限已经开启");
|
||||
} else {
|
||||
console.log("日历权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(EKEventStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断备忘录权限是否开启
|
||||
function judgeIosPermissionMemo() {
|
||||
var result = false;
|
||||
var EKEventStore = plus.ios.import("EKEventStore");
|
||||
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
|
||||
if (ekAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("备忘录权限已经开启");
|
||||
} else {
|
||||
console.log("备忘录权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(EKEventStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Android权限查询
|
||||
function requestAndroidPermission(permissionID) {
|
||||
return new Promise((resolve, reject) => {
|
||||
plus.android.requestPermissions(
|
||||
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
|
||||
function(resultObj) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < resultObj.granted.length; i++) {
|
||||
var grantedPermission = resultObj.granted[i];
|
||||
console.log('已获取的权限:' + grantedPermission);
|
||||
result = 1
|
||||
}
|
||||
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
|
||||
var deniedPresentPermission = resultObj.deniedPresent[i];
|
||||
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
|
||||
result = 0
|
||||
}
|
||||
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
|
||||
var deniedAlwaysPermission = resultObj.deniedAlways[i];
|
||||
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
|
||||
result = -1
|
||||
}
|
||||
resolve(result);
|
||||
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
|
||||
// if (result != 1) {
|
||||
// gotoAppPermissionSetting()
|
||||
// }
|
||||
},
|
||||
function(error) {
|
||||
console.log('申请权限错误:' + error.code + " = " + error.message);
|
||||
resolve({
|
||||
code: error.code,
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 使用一个方法,根据参数判断权限
|
||||
function judgeIosPermission(permissionID) {
|
||||
if (permissionID == "location") {
|
||||
return judgeIosPermissionLocation()
|
||||
} else if (permissionID == "camera") {
|
||||
return judgeIosPermissionCamera()
|
||||
} else if (permissionID == "photoLibrary") {
|
||||
return judgeIosPermissionPhotoLibrary()
|
||||
} else if (permissionID == "record") {
|
||||
return judgeIosPermissionRecord()
|
||||
} else if (permissionID == "push") {
|
||||
return judgeIosPermissionPush()
|
||||
} else if (permissionID == "contact") {
|
||||
return judgeIosPermissionContact()
|
||||
} else if (permissionID == "calendar") {
|
||||
return judgeIosPermissionCalendar()
|
||||
} else if (permissionID == "memo") {
|
||||
return judgeIosPermissionMemo()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 跳转到**应用**的权限页面
|
||||
function gotoAppPermissionSetting() {
|
||||
if (isIos) {
|
||||
var UIApplication = plus.ios.import("UIApplication");
|
||||
var application2 = UIApplication.sharedApplication();
|
||||
var NSURL2 = plus.ios.import("NSURL");
|
||||
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
|
||||
var setting2 = NSURL2.URLWithString("app-settings:");
|
||||
application2.openURL(setting2);
|
||||
|
||||
plus.ios.deleteObject(setting2);
|
||||
plus.ios.deleteObject(NSURL2);
|
||||
plus.ios.deleteObject(application2);
|
||||
} else {
|
||||
// console.log(plus.device.vendor);
|
||||
var Intent = plus.android.importClass("android.content.Intent");
|
||||
var Settings = plus.android.importClass("android.provider.Settings");
|
||||
var Uri = plus.android.importClass("android.net.Uri");
|
||||
var mainActivity = plus.android.runtimeMainActivity();
|
||||
var intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
mainActivity.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查系统的设备服务是否开启
|
||||
// var checkSystemEnableLocation = async function () {
|
||||
function checkSystemEnableLocation() {
|
||||
if (isIos) {
|
||||
var result = false;
|
||||
var cllocationManger = plus.ios.import("CLLocationManager");
|
||||
var result = cllocationManger.locationServicesEnabled();
|
||||
console.log("系统定位开启:" + result);
|
||||
plus.ios.deleteObject(cllocationManger);
|
||||
return result;
|
||||
} else {
|
||||
var context = plus.android.importClass("android.content.Context");
|
||||
var locationManager = plus.android.importClass("android.location.LocationManager");
|
||||
var main = plus.android.runtimeMainActivity();
|
||||
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
|
||||
var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
|
||||
console.log("系统定位开启:" + result);
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
judgeIosPermission: judgeIosPermission,
|
||||
requestAndroidPermission: requestAndroidPermission,
|
||||
checkSystemEnableLocation: checkSystemEnableLocation,
|
||||
gotoAppPermissionSetting: gotoAppPermissionSetting
|
||||
}
|
83
src/uni_modules/all-speech/package.json
Normal file
83
src/uni_modules/all-speech/package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "all-speech",
|
||||
"displayName": "allspeech长按录音动画组件,多端权限判断,可监听开始、结束、取消事件",
|
||||
"version": "1.1.2",
|
||||
"description": "H5、App、小程序兼容",
|
||||
"keywords": [
|
||||
"长按",
|
||||
"录音",
|
||||
"动画组件"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "n",
|
||||
"QQ浏览器(Android)": "n"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "n",
|
||||
"Edge": "n",
|
||||
"Firefox": "y",
|
||||
"Safari": "n"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
89
src/uni_modules/all-speech/readme.md
Normal file
89
src/uni_modules/all-speech/readme.md
Normal file
@ -0,0 +1,89 @@
|
||||
### nbVoiceRecord概述
|
||||
- 这是个基于uni-app 符合uni_modules 的插件
|
||||
- 无任何依赖、纯css动画
|
||||
- nb是NeverBug的意思
|
||||
|
||||
### 主要功能
|
||||
- 长按组件后弹出录音弹窗,松手完成录音,手指向上滑动可取消;
|
||||
- 支持各种自定义,如弹窗高度、宽度、各处文字甚至声纹波形的尺寸和颜色;
|
||||
- 已完成多端适配,自动根据授权情况提示完成授权、已获得授权才开始录音
|
||||
- endRecord回调事件附带录音文件
|
||||
|
||||
### 动画预览
|
||||
|
||||
- 默认样式
|
||||
|
||||

|
||||
|
||||
- 自定义按钮为圆形(红背景、白字)、弹窗为正方形
|
||||
|
||||

|
||||
|
||||
### 基本用法:
|
||||
|
||||
```
|
||||
<template>
|
||||
<view>
|
||||
<all-speech @startRecord="start" @endRecord="end" @cancelRecord="cancel"></all-speech>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
methods: {
|
||||
start() {
|
||||
// 开始录音
|
||||
},
|
||||
end(event) {
|
||||
// 结束录音并处理得到的录音文件
|
||||
// event中,app端仅有tempFilePath字段,微信小程序还有duration和fileSize两个字段
|
||||
},
|
||||
cancel() {
|
||||
// 用户取消录音
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
### 全部支持参数
|
||||
|
||||
| 参数名 | 类型 | 默认值 | 作用 | 注意事项 |
|
||||
| ----- | ----- | ------ | ------- | --- |
|
||||
| recordOptions | Object | {duration:60000} | 录音配置 |各端支持情况不同,请自行查看[官方说明](https://uniapp.dcloud.net.cn/api/media/record-manager.html#getrecordermanager) |
|
||||
| btnStyle | Object | 请查看源码 | 按钮样式 |对象格式 |
|
||||
| btnHoverFontcolor | String | #000 | 按钮长按时文字颜色 | |
|
||||
| btnHoverBgcolor | String | whitesmoke | 按钮长按时背景颜色 | |
|
||||
| btnDefaultText | String | 长按开始录音 | 初始按钮文字 | |
|
||||
| btnRecordingText | String | 录音中 | 录制时按钮文字 | |
|
||||
| vibrate | Boolean | true | 震动反馈 | 弹窗、滑动取消时 |
|
||||
| popupTitle | String | 正在录制音频 | 弹窗顶部文字 | |
|
||||
| popupDefaultTips | String | 松手完成录音 | 录制时弹窗底部提示 | |
|
||||
| popupCancelTips | String | 松手取消录音 | 滑动取消时弹窗底部提示 | |
|
||||
| popupMaxWidth | Number | 600 | 弹窗展开后宽度 |注意这里几个单位都是rpx |
|
||||
| popupMaxHeight | Number | 300 | 弹窗展开后高度 | |
|
||||
| popupFixBottom | Number | 200 | 弹窗展开后距底部高度 | |
|
||||
| popupBgColor | String | whitesmoke | 弹窗背景颜色 | |
|
||||
| lineHeight | Number | 50 | 声波高度 | |
|
||||
| lineStartColor | String | royalblue | 声波波谷时颜色色值 | 色值或者颜色名均可 |
|
||||
| lineEndColor | String | indianred | 声波波峰时颜色色值 | |
|
||||
|
||||
### 原作者其他插件
|
||||
|
||||
- [bwinBrand多端自适应企业官网、uniCloud云端一体【用户端】](https://ext.dcloud.net.cn/plugin?id=7821)
|
||||
- [bwinBrand多端自适应企业官网、uniCloud云端一体【管理端】](https://ext.dcloud.net.cn/plugin?id=7822)
|
||||
- [bwinAgent多端、多项目全民经纪人、uniCloud云端一体【经纪人端】](https://ext.dcloud.net.cn/plugin?id=8606)
|
||||
- [bwinAgent多端、多项目全民经纪人、uniCloud云端一体【管理员端】](https://ext.dcloud.net.cn/plugin?id=8607)
|
||||
- [必闻优学,教育培训机构模板(单校区版,纯模板)](https://ext.dcloud.net.cn/plugin?id=7709)
|
||||
|
||||
### 一个有趣的社区
|
||||
|
||||
- [NeverBug.cn 弹幕式互动社区](https://neverbug.cn)
|
||||
|
||||
### 原作者
|
||||
- QQ:123060128
|
||||
- Email:karma.zhao@gmail.com
|
||||
- 官网:https://brand.neverbug.cn
|
||||
|
||||
### 补充作者
|
||||
- QQ:27836407
|
||||
- Email:zgdabao.zhao@gmail.com
|
@ -188,5 +188,9 @@
|
||||
"submitting": "提交中...",
|
||||
"success": "投诉提交成功"
|
||||
}
|
||||
}
|
||||
},
|
||||
"release_hand_to_send": "松开发送",
|
||||
"release_hand_to_cancel": "松开取消",
|
||||
"hold_to": "按住",
|
||||
"speak": "说话"
|
||||
}
|
||||
|
@ -213,3 +213,64 @@ export function handleSetWebviewStyle(hasTabBar) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 通用运算函数
|
||||
/*
|
||||
函数,加法函数,用来得到精确的加法结果
|
||||
说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
|
||||
参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
|
||||
调用:Calc.Add(arg1,arg2,d)
|
||||
返回值:两数相加的结果
|
||||
*/
|
||||
export function addition(arg1, arg2) {
|
||||
arg1 = arg1.toString(), arg2 = arg2.toString();
|
||||
var arg1Arr = arg1.split("."),
|
||||
arg2Arr = arg2.split("."),
|
||||
d1 = arg1Arr.length == 2 ? arg1Arr[1] : "",
|
||||
d2 = arg2Arr.length == 2 ? arg2Arr[1] : "";
|
||||
var maxLen = Math.max(d1.length, d2.length);
|
||||
var m = Math.pow(10, maxLen);
|
||||
var result = Number(((arg1 * m + arg2 * m) / m).toFixed(maxLen));
|
||||
var d = arguments[2];
|
||||
return typeof d === "number" ? Number((result).toFixed(d)) : result;
|
||||
}
|
||||
|
||||
/*
|
||||
函数:减法函数,用来得到精确的减法结果
|
||||
说明:函数返回较为精确的减法结果。
|
||||
参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
|
||||
调用:Calc.Sub(arg1,arg2)
|
||||
返回值:两数相减的结果
|
||||
*/
|
||||
|
||||
/*
|
||||
函数:乘法函数,用来得到精确的乘法结果
|
||||
说明:函数返回较为精确的乘法结果。
|
||||
参数:arg1:第一个乘数;arg2第二个乘数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
|
||||
调用:Calc.Mul(arg1,arg2)
|
||||
返回值:两数相乘的结果
|
||||
*/
|
||||
export function multiplication(arg1, arg2) {
|
||||
var r1 = arg1.toString(),
|
||||
r2 = arg2.toString(),
|
||||
m, resultVal, d = arguments[2];
|
||||
m = (r1.split(".")[1] ? r1.split(".")[1].length : 0) + (r2.split(".")[1] ? r2.split(".")[1].length : 0);
|
||||
resultVal = Number(r1.replace(".", "")) * Number(r2.replace(".", "")) / Math.pow(10, m);
|
||||
return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
|
||||
}
|
||||
|
||||
/*
|
||||
函数:除法函数,用来得到精确的除法结果
|
||||
说明:函数返回较为精确的除法结果。
|
||||
参数:arg1:除数;arg2被除数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
|
||||
调用:Calc.Div(arg1,arg2)
|
||||
返回值:arg1除于arg2的结果
|
||||
*/
|
||||
export function division(arg1, arg2) {
|
||||
var r1 = arg1.toString(),
|
||||
r2 = arg2.toString(),
|
||||
m, resultVal, d = arguments[2];
|
||||
m = (r2.split(".")[1] ? r2.split(".")[1].length : 0) - (r1.split(".")[1] ? r1.split(".")[1].length : 0);
|
||||
resultVal = Number(r1.replace(".", "")) / Number(r2.replace(".", "")) * Math.pow(10, m);
|
||||
return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user