
简体中文
录音管理
Web | 微信小程序 | Android | Android uni-app x UTS 插件 | iOS | iOS uni-app x UTS 插件 | HarmonyOS |
---|---|---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 | 4.61 | 4.61 |
类型 |
---|
RecorderManager |
开始录音
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options | RecorderManagerStartOptions | 是 | - | - | - | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
暂停录音,App-Android平台在Android 7.0及以后版本支持
Web | 微信小程序 | Android 系统版本 | Android | iOS | HarmonyOS |
---|---|---|---|---|---|
x | 4.41 | 7.0 | 4.61 | 4.61 | 4.61 |
继续录音,App-Android平台在Android 7.0及以后版本支持
Web | 微信小程序 | Android 系统版本 | Android | iOS | HarmonyOS |
---|---|---|---|---|---|
x | 4.41 | 7.0 | 4.61 | 4.61 | 4.61 |
停止录音
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
录音开始事件
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
录音暂停事件,App-Android平台在Android 7.0及以后版本支持
Web | 微信小程序 | Android 系统版本 | Android | iOS | HarmonyOS |
---|---|---|---|---|---|
x | 4.41 | 7.0 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
录音停止事件,会回调文件地址
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: RecorderManagerOnStopResult) => void | 是 | - | - | - |
名称 | 类型 | 必备 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
tempFilePath | string | 是 | - | 录音文件的临时路径 |
已录制完指定帧大小的文件,会回调录音分片结果数据。如果设置了 frameSize ,则会回调此事件
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | x | x | x |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
录音错误事件, 会回调错误信息
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
监听录音继续事件,App-Android平台在Android 7.0及以后版本支持
Web | 微信小程序 | Android 系统版本 | Android | iOS | HarmonyOS |
---|---|---|---|---|---|
x | 4.41 | 7.0 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
监听录音因为受到系统占用而被中断开始事件
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
监听录音中断结束事件
Web | 微信小程序 | Android | iOS | HarmonyOS |
---|---|---|---|---|
x | 4.41 | 4.61 | 4.61 | 4.61 |
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
options | (result: any) => void | 是 | - | - | - |
编码格式与采样率、码率的关系
Android、iOS、微信小程序
采样率 | 编码码率 |
---|---|
8000 | 16000 - 48000 |
11025 | 16000 - 48000 |
12000 | 24000 - 64000 |
16000 | 24000 - 96000 |
22050 | 32000 - 128000 |
24000 | 32000 - 128000 |
32000 | 48000 - 192000 |
44100 | 64000 - 320000 |
48000 | 64000 - 320000 |
HarmonyOS
该 API 不支持 Web,请运行 hello uni-app x 到 App 平台体验
<template>
<page-head :title="title"></page-head>
<view class="page-body-time">
<text class="time-big">{{formatedRecordTime}}</text>
<!-- <text class="time-big">{{recordTimeInterval}}</text> -->
</view>
<scroll-view style="flex: 1;">
<view>
<button class="btnstyle" size="default" @click="registerOnStart">注册onStart</button>
<button class="btnstyle" size="default" @click="registeronStop">注册onStop</button>
<button class="btnstyle" size="default" id="btn-error" @click="registeronError">注册onError</button>
<button class="btnstyle" size="default" @click="registeronPause">注册onPause</button>
<button class="btnstyle" size="default" @click="registeronResume">注册onResume</button>
<button class="btnstyle" size="default" @click="registeronInterruptionBegin">注册onInterruptionBegin</button>
<button class="btnstyle" size="default" @click="registeronInterruptionEnd">注册onInterruptionEnd</button>
<view class="uni-list">
<text style="margin-bottom: 10px"> 请选择录音格式:</text>
<radio-group class="uni-flex uni-row" @change="radioChange" style="flex-wrap: wrap">
<radio class="uni-list-cell" style="margin-right: 15px" v-for="(item, index) in items" :key="item.value"
:value="item.value" :checked="index === current">
{{ item.name }}
</radio>
</radio-group>
</view>
<button class="btnstyle" size="default" :disabled="disableStartBtn" id="btn-startRecord"
@click="startRecord()">开始录制</button>
<button class="btnstyle" size="default" :disabled="disablePauseBtn" @click="pauseRecord">暂停录制</button>
<button class="btnstyle" size="default" :disabled="disableResumeBtn" @click="resumeRecord">继续录制</button>
<button class="btnstyle" size="default" id="btn-stopRecord" @click="stopRecord">停止录制</button>
<button class="btnstyle" size="default" id="btn-startPlay" @click="playVoice">开始播放</button>
<button class="btnstyle" size="default" id="btn-stopPlay" @click="stopVoice">停止播放</button>
</view>
</scroll-view>
</template>
<script>
export type ItemType = { value : string, name : string }
export default {
data() {
return {
disableStartBtn: false,
disableResumeBtn: false,
disablePauseBtn: false,
title: 'start/stopRecord、play/stopVoice',
recording: false, //录音中
playing: false, //播放中
registerError: false,
hasRecord: false, //是否有了一个
playTimeInterval: 0,
recordTimeInterval: 0,
tempFilePath: '',
recordTime: 0,
current: 0,
playTime: 0,
formatedRecordTime: '00:00:00', //录音的总时间
formatedPlayTime: '00:00:00',//播放录音的当前时间,
recorderManager: null as RecorderManager | null,
music: null as InnerAudioContext | null,
items: [
{
value: 'aac',
name: 'aac'
},
{
value: 'mp3',
name: 'mp3'
},
{
value: 'wav',
name: 'wav'
},
// #ifdef APP-HARMONY
{
value: 'm4a',
name: 'm4a'
},
// #endif
// #ifndef APP-HARMONY
{
value: 'pcm',
name: 'pcm'
}
// #endif
] as ItemType[]
}
},
onUnload: function () {
this.end();
},
onLoad: function () {
this.music = uni.createInnerAudioContext();
this.music!.onEnded(() => {
clearInterval(this.playTimeInterval)
var playTime = 0
console.log('play voice finished')
this.playing = false;
this.formatedPlayTime = this.formatTime(playTime);
this.playTime = playTime;
});
this.recorderManager = uni.getRecorderManager();
this.recorderManager!.onStart(() => {
console.log('recorder onStart');
this.disableStartBtn = true
this.disablePauseBtn = false
this.disableResumeBtn = false
this.recording = true;
this.recordTime = 0
this.recordTimeInterval = setInterval(() => {
this.recordTime += 1;
this.formatedRecordTime = this.formatTime(this.recordTime);
}, 1000)
});
this.recorderManager!.onStop((res) => {
console.log('on stop', res.tempFilePath);
this.disablePauseBtn = false
this.disableResumeBtn = false
this.disableStartBtn = false
this.music!.src = res.tempFilePath
clearInterval(this.recordTimeInterval)
this.hasRecord = true;
this.recording = false;
});
this.recorderManager!.onError((res) => {
console.log('recorder onError', JSON.stringify(res));
this.disablePauseBtn = false
this.disableResumeBtn = false
this.disableStartBtn = false
this.registerError = true
uni.showToast({
title: res as string
})
});
},
methods: {
radioChange(e : UniRadioGroupChangeEvent) {
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].value === e.detail.value) {
this.current = i;
break;
}
}
},
registerOnStart() {
uni.showToast({
title: 'already registerOnStart'
})
this.recorderManager!.onStart(() => {
console.log('recorder on start');
this.recordTime = 0
this.recording = true;
this.recordTimeInterval = setInterval(() => {
this.recordTime += 1;
this.formatedRecordTime = this.formatTime(this.recordTime);
}, 1000)
});
},
registeronStop() {
uni.showToast({
title: 'already registeronStop'
})
this.recorderManager!.onStop((res) => {
console.log('on stop', res.tempFilePath);
this.music!.src = res.tempFilePath
clearInterval(this.recordTimeInterval)
this.hasRecord = true;
this.recording = false;
});
},
registeronError() {
uni.showToast({
title: 'already registeronError'
})
this.registerError = true
this.recorderManager!.onError((res) => {
console.log('recorder onError', JSON.stringify(res));
});
},
registeronPause() {
uni.showToast({
title: 'already registeronPause'
})
this.recorderManager?.onPause(() => {
console.log('recorder onPause');
})
},
registeronResume() {
uni.showToast({
title: 'already registeronStop'
})
this.recorderManager?.onResume(() => {
console.log('recorder onResume');
})
},
registeronOnFrameRecorded() {
uni.showToast({
title: 'already registeronOnFrameRecorded'
})
this.recorderManager?.onFrameRecorded((res) => {
console.log('recorder onFrameRecorded----', res);
})
},
registeronInterruptionBegin() {
uni.showToast({
title: 'already registeronInterruptionBegin'
})
this.recorderManager?.onInterruptionBegin(() => {
console.log('recorder onInterruptionBegin');
})
},
registeronInterruptionEnd() {
uni.showToast({
title: 'already registeronInterruptionEnd'
})
this.recorderManager?.onInterruptionBegin(() => {
console.log('recorder registeronInterruptionEnd');
})
},
pauseRecord() {
console.log('recorder pause');
this.recorderManager?.pause()
if (this.recording) {
this.disableStartBtn = false
this.disablePauseBtn = true
this.disableResumeBtn = false
}
clearInterval(this.recordTimeInterval)
},
resumeRecord() {
console.log('recorder resume ', this.recorderManager);
this.recorderManager?.resume()
this.recorderManager?.onResume(() => {
console.log('recorder onResume');
})
if (this.recording) {
this.disableStartBtn = false
this.disablePauseBtn = false
this.disableResumeBtn = true
this.recordTimeInterval = setInterval(() => {
this.recordTime += 1;
this.formatedRecordTime = this.formatTime(this.recordTime);
}, 1000)
}
},
formatTime(time : number) : string {
if (typeof time !== 'number' || time < 0) {
return time.toString()
}
var hour = parseInt((time / 3600).toString())
time = time % 3600
var minute = parseInt((time / 60).toString())
time = time % 60
var second = time
return [hour, minute, second].map((n : number) => {
let str = n.toString();
return str.length > 1 ? str : "0" + str;
}).join(":");
},
startRecord() { //开始录音
if (this.recording) {
uni.showToast({
title: this.disablePauseBtn ? "当前是录音暂停状态" : "当前正在录音"
})
return
}
console.log('startRecord', this.items[this.current].value)
// TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用录音,但没有状态或回调判断用户拒绝
this.recorderManager?.start({
format: this.items[this.current].value,
sampleRate: 8000,
numberOfChannels: 2,
encodeBitRate: 48000,
frameSize: 2
});
},
stopRecord() { //停止录音
this.recorderManager?.stop();
this.disableStartBtn = false
this.disablePauseBtn = false
this.disableResumeBtn = false
},
playVoice() {
if (this.recording) {
uni.showToast({
title: "当前录音还未结束"
})
return
}
console.log('play voice');
if (this.playing) {
return
}
this.playing = true;
this.playTimeInterval = setInterval(() => {
if (this.playTime < this.recordTime) {
this.playTime += 1;
}
this.formatedRecordTime = this.formatTime(this.playTime);
}, 1000)
this.music?.play();
},
stopVoice() {
if (this.recording) {
uni.showToast({
title: "当前录音还未结束"
})
return
}
clearInterval(this.playTimeInterval)
this.playing = false;
this.formatedRecordTime = this.formatTime(0);
this.playTime = 0;
this.music?.stop();
},
end() {
this.music?.stop();
this.recorderManager?.stop();
clearInterval(this.recordTimeInterval)
clearInterval(this.playTimeInterval);
this.recording = false
this.playing = false
this.hasRecord = false;
this.playTime = 0
this.recordTime = 0;
this.formatedRecordTime = "00:00:00"
this.formatedRecordTime = "00:00:00";
},
clear() {
this.end();
}
}
}
</script>
<style>
.page-body-time {
display: flex;
flex-direction: column;
align-items: center;
}
.time-big {
font-size: 60rpx;
margin: 20rpx;
}
.btnstyle {
margin-left: 30px;
margin-right: 30px;
margin-top: 10px;
}
.uni-list {
border-bottom: 0px;
background-color: transparent;
margin-left: 30px;
margin-right: 30px;
margin-top: 10px;
margin-bottom: 10px;
}
</style>
名称 | 类型 | 必备 | 默认值 | 兼容性 | 描述 |
---|---|---|---|---|---|
errMsg | string | 是 | - | 错误信息 |