This commit is contained in:
yumoqing 2024-01-30 18:11:44 +08:00
parent 76abe0935f
commit a47d155bf9
7 changed files with 142 additions and 60 deletions

View File

@ -1,4 +1,16 @@
var formatMs=function(ms,all){
var ss=ms%1000;ms=(ms-ss)/1000;
var s=ms%60;ms=(ms-s)/60;
var m=ms%60;ms=(ms-m)/60;
var h=ms;
var t=(h?h+":":"")
+(all||h+m?("0"+m).substr(-2)+":":"")
+(all||h+m+s?("0"+s).substr(-2)+"″":"")
+("00"+ss).substr(-3);
return t;
};
class AudioPlayer extends JsWidget {
/*
{
@ -35,6 +47,7 @@ class AudioPlayer extends JsWidget {
}
this.player.source.src = url;
//webkitURL is deprecated but nevertheless
var URL = window.URL || window.webkitURL;
var AudioContext = window.AudioContext || window.webkitAudioContext;
@ -44,66 +57,135 @@ class AudioRecorder extends HBox {
super(opts);
this.start_icon = opts.start_icon || bricks_resource('imgs/start_recording.png');
this.stop_icon = opts.stop_icon || bricks_resource('imgs/stop_recording.png');
console.log('this.start_icon=', this.start_icon, this.stop_icon);
this.gumStream = null; //stream from getUserMedia()
this.recorder = null;
// shim for AudioContext when it's not avb.
this.img = new Image({height:'100%', url:this.start_icon});
this.player = new AudioPlayer({});
this.player.disabled(true);
this.add_widget(this.img);
this.add_widget(this.player);
this.img.bind('click', this.btn_clicked.bind(this));
this.audioContext = null;
this.stream = null;
this.chunks = [];
this.rec = null;
this.wave = null;
this.recBlob = null;
this.recOpen();
}
on_process(buffers,powerLevel,
bufferDuration,bufferSampleRate,
newBufferIdx,asyncEnd){
//录音实时回调大约1秒调用12次本回调
document.querySelector(".recpowerx").style.width=powerLevel+"%";
document.querySelector(".recpowert").innerText=formatMs(bufferDuration,1)+" / "+powerLevel;
//可视化图形绘制
wave.input(buffers[buffers.length-1],powerLevel,bufferSampleRate);
}
recOpen(){
this.rec=null;
this.wave=null;
this.recBlob=null;
var newRec=Recorder({
type:"wav",sampleRate:16000,bitRate:16
,onProcess:this.on_process.bind(this)
}
});
newRec.open(function(){//打开麦克风授权获得相关资源
this.rec=newRec;
//此处创建这些音频可视化图形绘制浏览器支持妥妥的
this.wave=Recorder.FrequencyHistogramView({elem:".recwave"});
}.bind(this),function(msg,isUserNotAllow){//用户拒绝未授权或不支持
console.log('open recorder failed');
});
}
recClose(){
if(this.rec){
this.rec.close();
}else{
console.log('close recorder error');
};
}
start_recording(){
console.log("recordButton clicked", this);
var constraints = { audio: true, video:false }
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js ...", this);
this.img.set_url(this.stop_icon);
this.audioContext = new AudioContext();
this.gumStream = stream;
/* use the stream */
try {
var input = this.audioContext.createMediaStreamSource(stream);
} catch(e){
console.log('e====', e);
}
this.recorder = new Recorder(input,{numChannels:1})
this.recorder.record()
console.log("Recording started");
}.bind(this)).catch(function(err) {
//enable the record button if getUserMedia() fails
this.img.set_url(this.start_icon);
}.bind(this));
if(this.rec&&Recorder.IsOpen()){
this.recBlob=null;
this.rec.start();
}else{
console.log("ajKR::未打开录音");
};
}
pause_recording(){
if(this.rec&&Recorder.IsOpen()){
this.rec.pause();
}else{
console.log("gCAR::未打开录音");
};
}
resume_recording(){
if(this.rec&&Recorder.IsOpen()){
this.rec.resume();
}else{
console.log("Ob6S::未打开录音");
};
}
stop_recording(){
this.img.set_url(this.start_icon);
this.recorder.stop();
this.gumStream.getAudioTracks()[0].stop();
this.recorder.exportWAV(this.createWAV.bind(this));
if(!(this.rec&&Recorder.IsOpen())){
console.log("5JuL::未打开录音");
return;
};
this.rec.stop(function(blob,duration){
console.log(blob,(window.URL||webkitURL).createObjectURL(blob),Html_xT(Html_$T("gOix::时长:{1}ms",0,duration)));
this.recBlob=blob;
console.log("0LHf::已录制mp3{1}ms {2}字节,可以点击播放、上传、本地下载了",0,formatMs(duration),blob.size);
}.bind(this),function(msg){
console.log("kGZO::录音失败:");
});
}
createWAV(blob){
console.log('reord stoped .....', this);
var url = URL.createObjectURL(blob);
this.player.set_source(url);
this.player.disabled(false);
console.log('---------done------------', url);
play_record(){
if(!this.recBlob){
console.log("tIke::请先录音,然后停止后再播放");
return;
};
var cls=("a"+Math.random()).replace(".","");
var audio=document.createElement("audio");
audio.controls=true;
document.querySelector("."+cls).appendChild(audio);
//简单利用URL生成播放地址注意不用了时需要revokeObjectURL否则霸占内存
audio.src=(window.URL||webkitURL).createObjectURL(recBlob);
audio.play();
setTimeout(function(){
(window.URL||webkitURL).revokeObjectURL(audio.src);
},5000);
}
btn_clicked(){
if (!this.isRecording){
this.img.src = this.stop_icon;
this.isRecording = true;
this.start_recording();
} else {
this.img.src = this.start_icon;
this.isRecording = false;
this.stop_recording();
upload(){
var blob=this.recBlob;
if(!blob){
console.log("DUTn::请先录音,然后停止后再上传");
return;
};
var form=new FormData();
form.append("audiofile",blob,"recorder.wav");
const data = new URLSearchParams();
for (const pair of form)) {
data.append(pair[0], pair[1]);
}
hj = HttpJson()
ret = await hj.post(this.upload_url,{
params:data
});
console.log('ret=', ret);
this.dispatch('updated', ret)
}
download(){
if(!this.recBlob){
console.log('recorder not opened');
return;
};
var fileName="recorder-"+Date.now()+".wav";
var downA=document.createElement("A");
downA.innerHTML="下载")+fileName;
downA.href=(window.URL||webkitURL).createObjectURL(this.recBlob);
downA.download=fileName;
# document.querySelector("."+cls).appendChild(downA);
if(/mobile/i.test(navigator.userAgent)){
;
}
downA.click();
//不用了时需要revokeObjectURL否则霸占内存
(window.URL||webkitURL).revokeObjectURL(downA.href);
}
}

View File

@ -7,6 +7,7 @@
<link rel="stylesheet" href="/bricks/css/bricks.css">
</head>
<body>
<script src="/3parties/recorder.wav.min.js"></script>
<script src="/bricks/bricks.js"></script>
<script>
const opts = {

BIN
bricks/imgs/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
bricks/imgs/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
bricks/imgs/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -40,15 +40,17 @@ class HttpText {
add_own_params(params){
if (! params)
params = {};
var session = bricks_app.get_session();
if (params instanceof FormData){
for ( const [key, value] of Object.entries(this.params)){
params.append(key, value);
}
if(session){
params.append('session', session);
}
return params;
}
else {
params = Object.assign(this.params, params);
}
var session = bricks_app.get_session();
params = Object.assign(this.params, params);
if (session){
extend(params,{session:session});
}
@ -60,7 +62,6 @@ class HttpText {
}
return Object.assign(this.headers, headers);
}
async httpcall(url, {method='GET', headers=null, params=null}={}){
url = this.url_parse(url);
var data = this.add_own_params(params);
@ -93,8 +94,6 @@ class HttpText {
if (fetchResult.status == 401 && bricks_app.login_url){
return await this.withLoginInfo(url, _params);
}
console.log('method=', method, 'url=', url, 'params=', params);
console.log('jsoncall error:');
const resp_error = {
"type":"Error",
"message":result.message || 'Something went wrong',