forked from xiangyuecn/Recorder
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
增加GetContext、PowerDBFS,Recorder.CLog可以禁用日志输出,尝试修复iPhone14专有的AudioCont…
…ext.resume异常
- Loading branch information
1 parent
73fcf4b
commit e61c596
Showing
17 changed files
with
365 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/****************** | ||
《【Demo库】WAV文件解码》 | ||
作者:高坚果 | ||
时间:2023-01-11 16:37 | ||
文档: | ||
DemoFragment.DecodeWav(u8arr) | ||
u8arr: wav的二进制数据Uint8Array | ||
返回:{ | ||
pcm:[Int16,...] 解码出来的pcm数据Int16Array | ||
sampleRate:16000 wav的采样率,也是pcm的采样率 | ||
duration:123 时长 | ||
bitRate:16 wav的位数,注意:pcm固定16位 | ||
numChannels:1 wav的声道数,注意:pcm固定单声道 | ||
} | ||
解码失败会抛异常 | ||
******************/ | ||
( | ||
window.DemoFragment||(window.DemoFragment={}) | ||
).DecodeWav=function(u8arr){ | ||
var eq=function(p,s){ | ||
for(var i=0;i<s.length;i++){ | ||
if(u8arr[p+i]!=s.charCodeAt(i)){ | ||
return false; | ||
}; | ||
}; | ||
return true; | ||
}; | ||
if(eq(0,"RIFF")&&eq(8,"WAVEfmt ")){ | ||
var numCh=u8arr[22]; | ||
if(u8arr[20]==1 && (numCh==1||numCh==2)){//raw pcm | ||
var sampleRate=u8arr[24]+(u8arr[25]<<8)+(u8arr[26]<<16)+(u8arr[27]<<24); | ||
var bitRate=u8arr[34]+(u8arr[35]<<8); | ||
|
||
//搜索data块的位置 | ||
var dataPos=0; // 44 或有更多块 | ||
for(var i=12,iL=u8arr.length-4;i<iL;){ | ||
if(u8arr[i]==100&&u8arr[i+1]==97&&u8arr[i+2]==116&&u8arr[i+3]==97){//eq(i,"data") | ||
dataPos=i+8;break; | ||
} | ||
i+=4; | ||
i+=4+u8arr[i]+(u8arr[i+1]<<8)+(u8arr[i+2]<<16)+(u8arr[i+3]<<24); | ||
} | ||
if(!dataPos){ | ||
throw new Error("未找到wav的data块"); | ||
} | ||
|
||
//统一转成16位 | ||
if(bitRate==16){ | ||
var pcm=new Int16Array(u8arr.buffer.slice(dataPos)); | ||
}else if(bitRate==8){//8位转成16位 | ||
var pcm=new Int16Array(u8arr.length-dataPos); | ||
for(var i=dataPos,j=0;j<pcm.length;i++,j++){ | ||
pcm[j]=(u8arr[i]-128)<<8; | ||
}; | ||
}else{ | ||
throw new Error("只支持8位或16位的wav格式"); | ||
} | ||
//转成单声道 | ||
if(numCh==2){ | ||
var pcm1=new Int16Array(pcm.length/2); | ||
for(var i=0;i<pcm1.length;i++){ | ||
pcm1[i]=pcm[i*2]; | ||
} | ||
pcm=pcm1; | ||
} | ||
var duration=Math.round(pcm.length/sampleRate*1000); | ||
|
||
console.log("DecodeWav",sampleRate,bitRate,numCh | ||
,pcm.length | ||
,duration+"ms @"+dataPos); | ||
return { | ||
pcm:pcm | ||
,duration:duration | ||
,sampleRate:sampleRate | ||
,bitRate:bitRate | ||
,numChannels:numCh | ||
}; | ||
}; | ||
throw new Error("只支持单声道或双声道wav格式"); | ||
}; | ||
throw new Error("非wav格式音频"); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/****************** | ||
《【测试】IIR低通、高通滤波》 | ||
作者:高坚果 | ||
时间:2023-01-11 16:32 | ||
移植java代码测试,测试结果: DigitalAudioFilter 比 MinimIIRFilter 过滤的干净,需要的频率能量损失也更小 | ||
******************/ | ||
|
||
/******Java代码1******/ | ||
//https://gitee.com/52jian/digital-audio-filter/blob/master/src/main/java/com/zj/filter/AudioFilter.java | ||
//https://blog.csdn.net/Janix520/article/details/118411734 | ||
Recorder.DigitalAudioFilter=function(useLowPass, sampleRate, freq){ | ||
var Q=1; | ||
var ov = 2 * Math.PI * freq / sampleRate; | ||
var sn = Math.sin(ov); | ||
var cs = Math.cos(ov); | ||
var alpha = sn / (2 * Q); | ||
|
||
var a0 = 1 + alpha; | ||
var a1 = (-2 * cs) / a0; | ||
var a2 = (1 - alpha) / a0; | ||
if(useLowPass){ | ||
var b0 = (1 - cs) / 2 / a0; | ||
var b1 = (1 - cs) / a0; | ||
var b2 = (1 - cs) / 2 / a0; | ||
}else{ | ||
var b0 = (1 + cs) / 2 / a0; | ||
var b1 = -(1 + cs) / a0; | ||
var b2 = (1 + cs) / 2 / a0; | ||
} | ||
|
||
var x1=0,x2=0,y=0,y1=0,y2=0; | ||
return function(x){ | ||
y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; | ||
x2 = x1; | ||
x1 = x; | ||
y2 = y1; | ||
y1 = y; | ||
return y; | ||
}; | ||
}; | ||
|
||
|
||
/******Java代码2******/ | ||
//https://github.com/ddf/Minim/tree/master/src/main/java/ddf/minim/effects | ||
Recorder.MinimIIRFilter=function(useLowPass, sampleRate, freq){ | ||
var freqFrac = freq/sampleRate; | ||
if(useLowPass=="FS"){ //LowPassFS.java | ||
var x = Math.exp(-14.445 * freqFrac); | ||
var a = [ Math.pow(1 - x, 4) ]; | ||
var b = [ 4 * x, -6 * x * x, 4 * x * x * x, -x * x * x * x ]; | ||
}else if(useLowPass){ //LowPassSP.java | ||
var x = Math.exp(-2*Math.PI*freqFrac); | ||
var a=[ 1 - x ]; | ||
var b=[ x ]; | ||
}else{ //HighPassSP.java | ||
var x = Math.exp(-2 * Math.PI * freqFrac); | ||
var a = [ (1+x)/2, -(1+x)/2 ]; | ||
var b = [ x ]; | ||
} | ||
|
||
var out=[],ins=[]; | ||
for(var i=0,L=Math.max(a.length,b.length);i<L;i++){ | ||
out[i]=0; ins[i]=0; | ||
} | ||
return function(x){ //IIRFilter.java uGenerate | ||
ins.splice(0,0,x); | ||
ins.length--; | ||
|
||
var y = 0; | ||
for(var ci = 0; ci < a.length; ci++) { | ||
y += a[ci] * ins[ci]; | ||
} | ||
for(var ci = 0; ci < b.length; ci++) { | ||
y += b[ci] * out[ci]; | ||
} | ||
out.splice(0,0,y); | ||
out.length--; | ||
return y; | ||
}; | ||
}; | ||
|
||
|
||
|
||
|
||
|
||
|
||
//=====测试代码================== | ||
//加载录音框架 | ||
Runtime.Import([ | ||
{url:RootFolder+"/src/recorder-core.js",check:function(){return !window.Recorder}} | ||
,{url:RootFolder+"/src/engine/wav.js",check:function(){return !Recorder.prototype.wav}} | ||
,{url:RootFolder+"/assets/runtime-codes/fragment.decode.wav.js",check:function(){return !window.DemoFragment||!DemoFragment.DecodeWav}}//引入DemoFragment.DecodeWav | ||
]); | ||
|
||
//显示控制按钮 | ||
Runtime.Ctrls([ | ||
{html:'<div class="testChoiceFile"></div>'} | ||
,{html:` | ||
<div> | ||
<div>低通:<input class="in_lowPassHz" style="width:100px">Hz,不填不滤波<span class="maxHz"></span></div> | ||
<div>高通:<input class="in_highPassHz" style="width:100px">Hz,不填不滤波<span class="maxHz"></span></div> | ||
<div>采样率:<input class="in_sampleRate" style="width:100px">,不填不转换采样率<span class="maxSampleRate"></span></div> | ||
</div>`} | ||
,{name:"开始转换1",click:"test(1);Date.now"} | ||
,{name:"开始转换2",click:"test(2);Date.now"} | ||
,{name:"开始转换2_FS",click:"test(2,true);Date.now"} | ||
|
||
,{choiceFile:{multiple:false,title:"解码", | ||
process:function(fileName,arrayBuffer,filesCount,fileIdx,endCall){ | ||
if(/\.wav$/i.test(fileName)){ | ||
try{ | ||
var data=DemoFragment.DecodeWav(new Uint8Array(arrayBuffer)); | ||
}catch(e){ | ||
Runtime.Log(fileName+"解码失败:"+e.message,1); | ||
return endCall(); | ||
} | ||
setPcmData({pcm:data.pcm,sampleRate:data.sampleRate}); | ||
return endCall(); | ||
} | ||
Runtime.DecodeAudio(fileName,arrayBuffer,function(data){ | ||
setPcmData({pcm:data.data,sampleRate:data.sampleRate}); | ||
|
||
endCall(); | ||
},function(msg){ | ||
Runtime.Log(msg,1); | ||
endCall(); | ||
}); | ||
} | ||
}} | ||
]); | ||
|
||
$(".testChoiceFile").append($(".RuntimeChoiceFileBox")); | ||
var pcmData; | ||
var setPcmData=function(data){ | ||
pcmData=data; | ||
$(".maxHz").html("最高"+(data.sampleRate/2)+"Hz"); | ||
$(".maxSampleRate").html("最大"+data.sampleRate); | ||
|
||
var rec=Recorder({ | ||
type:"wav",bitRate:16,sampleRate:data.sampleRate | ||
}).mock(data.pcm,data.sampleRate); | ||
rec.stop(function(blob,duration){ | ||
Runtime.LogAudio(blob,duration,rec,"文件解码成功"); | ||
Runtime.Log("pcm数据已准备好,可以开始转换了,pcm.sampleRate="+data.sampleRate,2); | ||
}); | ||
}; | ||
|
||
var test=function(fn,useFS){ | ||
if(!pcmData){ | ||
Runtime.Log("请先拖一个文件进来解码",1); | ||
return; | ||
} | ||
var srcSampleRate=pcmData.sampleRate; | ||
var lowPassHz=+$(".in_lowPassHz").val()||0; | ||
var highPassHz=+$(".in_highPassHz").val()||0; | ||
var newSampleRate=+$(".in_sampleRate").val()||0; | ||
|
||
var lowPass=null,highPass=null,fnName=""; | ||
if(fn==1){ | ||
fnName="DigitalAudioFilter"; | ||
}else{ | ||
fnName="MinimIIRFilter"; | ||
} | ||
if(lowPassHz) | ||
lowPass=Recorder[fnName](useFS?"FS":true,srcSampleRate,lowPassHz); | ||
if(highPassHz) | ||
highPass=Recorder[fnName](false,srcSampleRate,highPassHz); | ||
|
||
var pcm=new Int16Array(pcmData.pcm.length); | ||
for(var i=0;i<pcm.length;i++){ | ||
var v=pcmData.pcm[i]; | ||
if(lowPass)v=lowPass(v); | ||
if(highPass)v=highPass(v); | ||
pcm[i]=v; | ||
} | ||
|
||
Runtime.Log("开始转换"+fn+" "+fnName+(useFS?".FS":"")+":"+JSON.stringify({lowPass:lowPassHz,highPass:highPassHz,sampleRate:newSampleRate,srcSampleRate:srcSampleRate}),"#aaa"); | ||
var rec=Recorder({ | ||
type:"wav",bitRate:16,sampleRate:newSampleRate||srcSampleRate | ||
}).mock(pcm,srcSampleRate); | ||
rec.stop(function(blob,duration){ | ||
Runtime.LogAudio(blob,duration,rec,"已转换"+fn); | ||
}); | ||
}; |
Oops, something went wrong.