var bricks = window.bricks || {}; bricks.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; }; bricks.AudioPlayer = class extends bricks.JsWidget { /* { url: autoplay: } */ constructor(options){ super(options); this.url = options.url; this.audio = this._create('audio'); this.audio.controls = true; if (this.opts.autoplay){ this.audio.addEventListener('canplay', this.play_audio.bind(this)); } this.audio.style.width = "100%" this.dom_element.appendChild(this.audio); if ( this.url ){ this.set_source(this.url); } } set_source(url){ if (! this.source){ this.source = this._create('source'); this.source.src = url; this.audio.appendChild(this.source); } this.url = this.audio.src = url; bricks.debug(this.audio.src,' new src seted'); } set_source_from_response(resp){ // 将服务器返回的数据设置为音频源 this.audio.src = URL.createObjectURL(new Blob([response.body])); // 播放音频 this.audio.play(); } play(){ this.audio.play(); } toggle_play(){ if (this.audio.paused){ this.audio.play(); } else { this.audio.pause(); } } set_url(url){ this.set_source(url); } } bricks.AudioRecorder = class extends bricks.HBox { /* { "upload_url": "icon_rate": } we need this module: https://gitee.com/xiangyuecn/Recorder events: record_started record_ended realtime record wave show need a another package we only use the wav file format */ constructor(opts){ 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'); this.rec_btn = new bricks.Icon({ dynsize:true, url:this.start_icon, rate:this.icon_rate }); // this.player = new bricks.AudioPlayer({url:"",width:'80px'}); this.rec_time = new bricks.Text({ text:" record time", i18n:false, dynsize:true, wrap:false, width:'120px' }); this.upload_url = opts.upload_url; this.rec_btn.bind('click', this.rec_btn_pressed.bind(this)) this.URL = window.URL||webkitURL; this.rec = null; this.wave = null; this.mic_opened = false; this.add_widget(this.rec_btn); this.add_widget(this.rec_time); this.recordData = null; this.bind('record_ended', this.upload.bind(this)); } rec_btn_pressed(){ bricks.debug(this.rec_btn.url, ':url:', this.start_icon, this.stop_icon); if(this.rec_btn.url == this.start_icon){ this.rec_btn.set_url(this.stop_icon); this.start_recording(); } else { this.rec_btn.set_url(this.start_icon); this.stop_recording(); } } on_process(buffers,powerLevel, bufferDuration,bufferSampleRate, newBufferIdx,asyncEnd){ //录音实时回调,大约1秒调用12次本回调 // document.querySelector(".recpowerx").style.width=powerLevel+"%"; //可视化图形绘制 // wave.input(buffers[buffers.length-1],powerLevel,bufferSampleRate); this.rec_time.set_text(' ' + bricks.formatMs(bufferDuration,1)); } recOpen(){ this.rec=null; this.wave=null; var newRec = Recorder({ type:"wav",sampleRate:16000,bitRate:16 ,onProcess:this.on_process.bind(this) }); newRec.open(function(){//打开麦克风授权获得相关资源 this.mic_opened = true; this.rec=newRec; this.rec.start(); this.dispatch('record_started'); //此处创建这些音频可视化图形绘制浏览器支持妥妥的 //this.wave=Recorder.FrequencyHistogramView({elem:".recwave"}); }.bind(this),function(msg,isUserNotAllow){//用户拒绝未授权或不支持 bricks.debug('open recorder failed'); }); } recClose(){ if(this.rec){ this.rec.close(); this.rec = null; this.mic_opened = false; }else{ bricks.debug('close recorder error'); }; } start_recording(){ this.recordData = null; if( this.recordData ){ this.URL.revokeObjectURL(this.recordData.url); } if (this.mic_opened){ this.rec.start(); this.dispatch('record_started'); } this.recOpen(); } pause_recording(){ if(this.rec&&Recorder.IsOpen()){ this.rec.pause(); }else{ bricks.debug("gCAR::未打开录音"); }; } resume_recording(){ if(this.rec&&Recorder.IsOpen()){ this.rec.resume(); }else{ bricks.debug("Ob6S::未打开录音"); }; } stop_recording(){ if(!(this.rec&&Recorder.IsOpen())){ bricks.debug("5JuL::未打开录音"); return; }; this.rec.stop(function(blob,duration){ var localURL = this.URL.createObjectURL(blob); var d = { data:blob, url:localURL, duration:duration } this.recordData = d; this.dispatch('record_ended', d); }.bind(this),function(msg){ bricks.debug("kGZO::录音失败:"); }); this.recClose(); } async upload(){ var running = new bricks.Running({target:this}); if(!this.recordData){ bricks.debug("DUTn::请先录音,然后停止后再上传"); return; }; if(!this.upload_url){ return; } var blob=this.recordData.data; bricks.debug('blob=', blob, this.recordData); var form=new FormData(); form.append("audiofile",blob,"recorder.wav"); var jpost = bricks.jpost var ret = await jpost(this.upload_url,{ params:form }); bricks.debug('ret=', ret); this.dispatch('uploaded', ret) running.dismiss(); } download(){ if(!this.recordData){ bricks.debug('recorder not opened'); return; }; var fileName="recorder-"+Date.now()+".wav"; var downA=document.createElement("A"); downA.innerHTML="下载"+fileName; downA.href=this.recordData.url; downA.download=fileName; // document.querySelector("."+cls).appendChild(downA); if(/mobile/i.test(navigator.userAgent)){ bricks.debug('mobile device'); } downA.click(); //不用了时需要revokeObjectURL,否则霸占内存 (window.URL||webkitURL).revokeObjectURL(downA.href); } } bricks.SttClient = class extends bricks.VBox { /* { "upload_url" } */ constructor(opts){ super(opts); this.recorder = new bricks.AudioRecorder({ icon_rate:2, upload_url:opts.upload_url, upload_resp:'json' }); this.add_widget(this.recorder); this.result_text = new bricks.Text({ text:'', dynsize:true }); this.add_widget(this.result_text); this.recorder.bind('uploaded', this.set_result_text.bind(this)); } set_result_text(event){ var txt = event.params.text; this.result_text.set_text(txt); } } bricks.Factory.register('AudioPlayer', bricks.AudioPlayer); bricks.Factory.register('AudioRecorder', bricks.AudioRecorder); bricks.Factory.register('SttClient', bricks.SttClient);