We can in the browser, by calling JS
native API
, converts speech to text, to achieve the effect of the voice input. The idea is:
- An audio recording;
- To convert the audio
URL
format string (base64
bit encoding); - Call iFlyTek open interface, the
base64
transition bit encoded as text.
This article realize the first two steps, the audio is converted to URL
the format string ( base64
bit coding).
Here it will be used in many media recording related API
lists it first:
MediaDevices
(MediaDevices
Use )MediaDevices
Interface provides access to the media input device is connected, such as a camera and a microphone, and a screen sharing.MediaDevices.getUserMedia()
Users will be prompted to give permission to use the media input.
We will want to visit microphone browser. If the browser supports getUserMedia
, you can access the microphone permission.
MediaDevices.getUserMedia()
Return to a Promise
subject, get a microphone permission, will be resolve
a callback MediaStream
object. MediaStream
It contains the input audio tracks.
MediaRecorder
(MediaRecorder
Use )MediaRecorder()
The constructor creates a the specifiedMediaStream
record ofMediaRecorder
the object.MediaStream
Is to be recorded stream. It can be derived from the use ofnavigator.mediaDevices.getUserMedia()
stream created.- Instantiated
MediaRecorder
object that provides an interface recording media
MediaRecorder()
The constructor accepts MediaDevices.getUserMedia()
resolve
callbacks MediaStream
, it will be recorded as a stream. And may specify MIMEType
the type and the audio bit rate.
The constructor is instantiated, the object can read the current state of the recording, and select the state of admission, pause, and stop.
MediaRecorder.stop()
Starting method will stop the recording, while the trigger dataavailable
event, returns a storage Blob
contents of recorded data, after which no record
Blob
(Blob
Use )Blob()
Constructor returns a new Blob object.Blob
It represents an immutable objects, the object class of the original data file.File
Interface is based onBlob
acceptingBlob
the object API also listed inFile
the document.
Blob()
Constructor takes MediaRecorder.ondataavailable()
method returns Blob
the type of recorded data, and specify the audio format.
After instantiating the constructor, creates a new immutable, the object class of the original data file.
URL.createObjectURL()
(URL.createObjectURL()
Use )URL.createObjectURL()
It creates a static methodDOMString
, which contains a URL object parameters given representation.- This new
URL
object representing the specifiedFile
object orBlob
objects.
URL.createObjectURL()
Accepts a Blob
object, creating a DomString
the string as <audio>
playing address elements.
FileReader
(FileReader
Use )FileReader()
Constructor to create a newFileReader
object.readAsDataURL()
Reads the specified methodBlob
orFile
object.- When the read operation is completed,
readyState
it will become a has been completedDONE
, and triggerloadend
events, while the result property will contain adata:URL
format string (base64
coding) to represent the contents of the file read.
Examples of the FileReader()
constructor, create a new FileReader
object.
Using the readAsDataURL()
method, a receiving Blob
object after reading is completed, trigger onload
method, while the result
property will contain a data: URL format string ( base64
code)
Use Angular
placing the core code is as follows:
QaComponent
<div id="voiceIcon" class="iconfont icon-voice" (click)="showVoice = !showVoice" [title]="showVoice ? '停止' : '录制'"></div>
<!-- 语音录制动画 -->
<app-voice [show]="showVoice"></app-voice>
showVoice = false; // 录音动画显示隐藏
/**
* 初始化完组件视图及其子视图之后,获取麦克风权限
*/
ngAfterViewInit(): void {
this.mediaRecorder();
}
/**
* 将语音文件转换为 base64 的字符串编码
*/
mediaRecorder() {
const voiceIcon = document.getElementById('voiceIcon') as HTMLDivElement;
// 在用户通过提示允许的情况下,打开系统上的麦克风
if (navigator.mediaDevices.getUserMedia) {
let chunks = [];
const constraints = { audio: true }; // 指定请求的媒体类型
navigator.mediaDevices.getUserMedia(constraints).then(
stream => {
// 成功后会resolve回调一个 MediaStream 对象,包含音频轨道的输入。
console.log('授权成功!');
const options = {
audioBitsPerSecond: 22050, // 音频的比特率
};
// MediaRecorder 构造函数实例化的 mediaRecorder 对象是用于媒体录制的接口
// @ts-ignore
const mediaRecorder = new MediaRecorder(stream, options);
voiceIcon.onclick = () => {
// 录制对象 MediaRecorder 的当前状态(闲置中 inactive,录制中 recording 或者暂停 paused)
if (mediaRecorder.state === 'recording') {
// 停止录制. 同时触发dataavailable事件,之后不再记录
mediaRecorder.stop();
console.log('录音结束');
} else {
// 开始录制媒体
mediaRecorder.start();
console.log('录音中...');
}
console.log('录音器状态:', mediaRecorder.state);
};
mediaRecorder.ondataavailable = (e: { data: any }) => {
// 返回一个存储Blob内容的录制数据,在事件的 data 属性中会提供一个可用的 Blob 对象
chunks.push(e.data);
};
mediaRecorder.onstop = () => {
// MIME类型 为 audio/wav
// 实例化 Blob 构造函数,返回的 blob 对象表示一个不可变、原始数据的类文件对象
const blob = new Blob(chunks, { type: 'audio/wav; codecs=opus' });
chunks = [];
// 如果作为音频播放,audioURL 是 <audio>元素的地址
const audioURL = window.URL.createObjectURL(blob);
const reader = new FileReader();
// 取指定的 Blob 或 File 对象,读取操作完成的时候,readyState 会变成已完成DONE
reader.readAsDataURL(blob);
reader.onload = () => {
// result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容
console.log(reader.result); // reader.result 为 base64 字符串编码
};
};
},
() => {
console.error('授权失败!');
},
);
} else {
console.error('浏览器不支持 getUserMedia');
}
}
VoiceComponent
<div class="voice-container" *ngIf="_show">
<i class="iconfont icon-voice"></i>
<div class="circle"></div>
</div>
.voice-container {
position: absolute;
top: 50%;
left: 50%;
z-index: 1;
transform: translate(-50%, -50%);
.icon-voice {
position: absolute;
top: 50%;
left: 50%;
z-index: 4;
display: block;
color: #fff;
font-size: 24px;
transform: translate(-50%, -50%);
}
.audio {
position: relative;
top: 50%;
left: 50%;
z-index: 4;
transform: translate(-50%, -50%);
}
.circle {
position: absolute;
top: 50%;
left: 50%;
z-index: 3;
border-radius: 50%;
transform: translate(-50%, -50%);
animation: gradient 1s infinite;
}
@keyframes gradient {
from {
width: 70px;
height: 70px;
background-color: rgb(24, 144, 255);
}
to {
width: 160px;
height: 160px;
background-color: rgba(24, 144, 255, 0.3);
}
}
}
public _show: boolean;
@Input()
set show(val: boolean) {
this._show = val;
}
get show() {
return this._show;
}