備忘錄_20160105(定位)
修改
回首頁
程式 2022-05-29 09:51:55 1653789115 100
製作類似卡拉OK字幕逐字亮起的流程─02聲音檔標記製作
製作類似卡拉OK字幕逐字亮起的流程─02聲音檔標記製作
- 用js+input file載入單機聲音檔(mp3)
- 解析聲音時,可以變更取樣頻率
- 可得知聲音檔的聲道數、總長時間
- 繪製聲音波形(全部,部分)
- 攔截mouse在canvas中的正確座標
- 攔截鍵盤keycode,ctrl,alt,shift,capslock也可攔截
- 重組聲音振福,播放部分聲音
- (若是用 Audacity,則可先拖曳選取範圍,再用 Ctrl+B 標上標籤,最後用匯出標籤。使用記事本讀取標籤,裏頭有秒數資訊。)
前往 20220526_loadmp3.htm
●20220526_loadmp3.htm
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*
{
font-family: "WenQuanYi Zen Hei","文泉驛正黑","Heiti TC","黑體-繁","LiHei Pro","儷黑 Pro","PingFang TC","Droid Sans","Roboto","Microsoft JhengHei","微軟正黑體",sans-serif;
/* 【linux的字型】【ios字型】【android字型】【微軟正黑體】【無襯線字=黑體】 */
}
html, body
{
margin: 0;
border: 0;
padding: 0;
width: 100%;
height: 100%;
}
a:link, a:visited
{
color: blue;
text-decoration: none;
}
#divWrapper
{
padding: 1em;
}
#divContainCanvas
{
display: inline-block;
margin: 1em;
border-width: 0;
padding: 1em;
background-color: #eef0a1;
}
.classBlock
{
display: inline-block;
padding: 12px;
border-style: solid;
border-width: 1px;
border-color: black;
border-radius: 6px;
background-color: #dddddd;
}
#divTimeBegin, #divTimeEnd
{
font-family: Consolas;
font-size: 24pt;
}
</style>
</head>
<body onkeydown="bodyKeydown(event);">
<div id="divWrapper">
<div>
<div class="classBlock">
變更取樣頻率:<input type="text" name="inpSR" id="inpSR" value="8000" style="width: 5em;">Hz (若為空白,則不進行變更)
</div>
<div class="classBlock">
檔案:<input type="file" name="oaAudioFile" id="oaAudioFile" onchange="loadAudioFile(this);">
</div>
</div>
<div id="divLoadStatus"></div>
<div style="padding: 6px;">
點選左鍵可產生一個標記,連續兩個標記即成為一個區塊。<br>
<div style="display: inline-block;">
<input type="checkbox" name="inpKeyboardCapture" id="inpKeyboardCapture" checked="true">捕捉按鍵
</div>
<button type="button" onclick="doCmd('leftArrow');">左移 [←]/[a]</button>
<button type="button" onclick="doCmd('rightArrow');">右移 [→]/[s]</button>
<button type="button" onclick="doCmd('+');">放大 [+]/[wheel up]</button>
<button type="button" onclick="doCmd('-');">縮小 [-]/[wheel down]</button>
<button type="button" onclick="doCmd('goto');">前往 [ctrl]+[g]</button>
<button type="button" onclick="rearrangeMarks();">重整數據</button>
<button type="button" onclick="doCmd('export');">重整數據並作匯出 [ctrl]+[y]</button>
<a>CapsLock模擬左鍵產生標記</a>
<div>
<div style="display: inline-block;">
聲量係數<input type="text" id="inpVolume" style="width: 3em;" placeholder="2 代表兩倍聲量" value="2">
</div>
<div style="display: inline-block;">
秒數區間用逗號分隔<input type="text" id="inpListenRange" style="width: 16em;" placeholder="空白即自動抓最後一個區塊" value="">
</div>
<button type="button" onclick="doCmd('q');">播放 [q]</button>
<button type="button" onclick="doCmd('w');">停止 [w]</button>
<button type="button" onclick="doCmd('e');">抓取滑鼠位置區塊並播放 [e]</button>
<button type="button" onclick="doCmd('d');">抓取滑鼠位置區塊並刪除 [d]</button>
<button type="button" onclick="doCmd('p');">抓取滑鼠位置區塊並試著分割 [p]</button>
</div>
<div class="classBlock">
【自動標記聲音區塊】
起始秒數<input type="text" id="inpAutoTimeBegin" value="0">
終止秒數<input type="text" id="inpAutoTimeEnd" value="0">
檢查區塊間隔秒數<input type="text" id="inpAutoTimeSpan" value="0.01">
最短區塊秒數<input type="text" id="inpAutoTimeBlockMinSecs" value="0.2">
振幅閥值(0~2)<input type="text" id="inpAutoThreshold" value="0.02">
<button type="button" onclick="doCmd('autoMarking');">執行自動標記</button>
</div>
<br>
<div class="classBlock">
【聲音區塊播放】
<button type="button" onclick="doCmd('search4indexofsoundblock');">重整數據,抓取滑鼠位置區塊並取得索引[r]</button>
索引(base0)<input type="text" id="inpIdxOfSoundBlock" value="0">
<button type="button" onclick="listenByIndex();">聽取並往下一個移動[n]</button>
<span id="spanListenByIndeStatus"></span>
</div>
</div>
<div id="divContainCanvas">
<div>
<div style="float: left;">
<div>標記暫存區</div>
<textarea id="taMarks" rows="8" cols="20"></textarea>
</div>
<div style="float: right;">
<div style="width: 1600px;">
<div id="divTimeBegin" style="display: inline-block; float: left;"></div>
<div id="divTimeCursor" style="display: inline-block; position: relative; left: 45%;"></div>
<div id="divTimeEnd" style="display: inline-block; float: right;"></div>
</div>
<canvas width="1600" height="100" id="myCanvas"
onmousemove="mouseMoving(event);"
onclick="mouseClick(event);"
onwheel="mouseWheel(event);"></canvas>
</div>
</div>
</div>
<div>
<div style="float: left;">
<div>聲音對應文字的沙盒</div>
<textarea id="taPlayground1" rows="8" cols="50"></textarea>
</div>
<div style="float: left;">
<div>準備要貼往EXCEL的輸出區</div>
<textarea id="taPlayground2" rows="8" cols="50"></textarea>
</div>
</div>
</div>
<div id="divHidden" style="display: none;">
<canvas width="1600" height="100" id="myBackupCanvas"></canvas>
</div>
<script>
if(!String.prototype.ltrim) { String.prototype.ltrim = function() { return this.replace(/^\s+/,''); }; }
if(!String.prototype.rtrim) { String.prototype.rtrim = function() { return this.replace(/\s+$/,''); }; }
if(!String.prototype.trim ) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,''); }; }
function gebi(strId)
{
return document.getElementById(strId);
}
var oAudioContext=null;
var oBufferForMp3=null;
var faAudioData=null;
var fTimeBegin=null, fTimeEnd=null;
var booLoadingAndDecoding=false;
var iIntervalId4LoadingAndDecoding=null;
function flashing()
{
if(booLoadingAndDecoding==false)
{
window.clearInterval(iIntervalId4LoadingAndDecoding);
return;
}
var r=Math.floor(Math.random()*255);
var g=Math.floor(Math.random()*255);
var b=Math.floor(Math.random()*255);
gebi("divLoadStatus").style.color="rgb("+r+","+g+","+b+")";
}
function loadAudioFile(oObj1)
{
gebi("divLoadStatus").innerHTML="準備載入音檔。";
booLoadingAndDecoding=true;
iIntervalId4LoadingAndDecoding=window.setInterval(flashing, 200);
var oFR=new FileReader();
oFR.onload=function()
{
gebi("divLoadStatus").innerHTML="已載入,準備開始解碼。";
var strSR=gebi("inpSR").value.trim();
if(strSR=="")
{ oAudioContext=new(window.AudioContext || window.webkitAudioContext); }
else
{ oAudioContext=new(window.AudioContext || window.webkitAudioContext)({ sampleRate: strSR*1 }); }
oAudioContext.decodeAudioData(
oFR.result,
(oData) =>
{
oBufferForMp3=oData;
faAudioData=Array.from(new Float32Array(oData.getChannelData(0)));
booLoadingAndDecoding=false;
gebi("divLoadStatus").style.color="black";
gebi("divLoadStatus").innerHTML
="解碼完成,"
+"sampleRate="+oBufferForMp3.sampleRate+"Hz,"
+"numberOfChannels="+oBufferForMp3.numberOfChannels+","
+"duration="+oBufferForMp3.duration+"秒,"
+"length="+oBufferForMp3.length+","
+"faAudioData.length="+faAudioData.length+"。";
fTimeBegin=0;
fTimeEnd=oBufferForMp3.duration;
drawWave(faAudioData, "white", "black");
drawMarks();
gebi("inpAutoTimeBegin").value=0;
gebi("inpAutoTimeEnd").value=oBufferForMp3.duration;
},
function(oError)
{
booLoadingAndDecoding=false;
gebi("divLoadStatus").style.color="red";
gebi("divLoadStatus").innerHTML="載入錯誤!"+oError;
});
}
gebi("divLoadStatus").innerHTML="載入中,請稍候,謝謝。";
oFR.readAsArrayBuffer(oObj1.files[0]); // 只讀取第一個檔案
}
window.addEventListener(
"load",
function()
{
}
);
</script>
<script>
function getStrFormattedSecs(fSeconds)
{
return Math.floor(fSeconds*100000)/100000;
}
function getStrFormattedTime(fSeconds)
{
var fHours=0;
var fMinutes=0;
var fValue;
fHours=Math.floor(fSeconds/3600);
fMinutes=Math.floor((fSeconds-fHours*3600)/60);
fValue=fSeconds-fHours*3600-fMinutes*60;
fValue=Math.floor(fValue*100000)/100000;
return fHours+":"+fMinutes+":"+fValue;
}
function drawWave(faAudioData, strFGColor, strBGColor)
{
if(faAudioData==null) { return; }
gebi("divTimeBegin").innerHTML=getStrFormattedSecs(fTimeBegin)+"<br>"+getStrFormattedTime(fTimeBegin);
gebi("divTimeEnd").innerHTML=getStrFormattedSecs(fTimeEnd)+"<br>"+getStrFormattedTime(fTimeEnd);
var oCanvas=gebi("myCanvas");
var oCtx=oCanvas.getContext("2d");
var iCanvasWidth=oCanvas.width;
var iCanvasHeight=oCanvas.height;
var iDataBegin=Math.floor(fTimeBegin/oBufferForMp3.duration*(faAudioData.length-1));
var iDataEnd=Math.floor(fTimeEnd/oBufferForMp3.duration*(faAudioData.length-1));
var iDataStep=Math.floor((iDataEnd-iDataBegin)/iCanvasWidth/2);
if(iDataStep<1) { iDataStep=1; }
oCtx.beginPath();
oCtx.rect(0,0,iCanvasWidth,iCanvasHeight);
oCtx.fillStyle=strBGColor;
oCtx.fill();
oCtx.strokeStyle=strFGColor;
oCtx.beginPath();
var fYBottom=-1;
var fYTop=1;
oCtx.moveTo(0,iCanvasHeight/2);
for(var iIdxData=iDataBegin; iIdxData<=iDataEnd; iIdxData+=iDataStep)
{
var x=(iIdxData-iDataBegin)/(iDataEnd-iDataBegin)*iCanvasWidth;
var fMin=1, fMax=-1;
for(var j=0; j<iDataStep; j++)
{
if(faAudioData[iIdxData+j]<fMin) { fMin=faAudioData[iIdxData+j]; }
if(faAudioData[iIdxData+j]>fMax) { fMax=faAudioData[iIdxData+j]; }
}
var yMin=(1-(fMin-fYBottom)/(fYTop-fYBottom))*iCanvasHeight;
oCtx.lineTo(x,yMin);
var yMax=(1-(fMax-fYBottom)/(fYTop-fYBottom))*iCanvasHeight;
oCtx.lineTo(x,yMax);
}
oCtx.stroke();
// backup the canvas
gebi("myBackupCanvas").getContext("2d").drawImage(oCanvas,0,0);
}
</script>
<script>
function bodyKeydown(oE)
{
var strCmd="";
console.log(oE);
if(oE.ctrlKey==true && oE.altKey==false && oE.shiftKey==false && oE.keyCode==192) { strCmd="`"; }
if(strCmd=="" && inpKeyboardCapture.checked==false)
{
console.log('good bye');
return;
}
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==107) { strCmd="+"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==109) { strCmd="-"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==true && oE.keyCode==61) { strCmd="+"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==true && oE.keyCode==173) { strCmd="-"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==37) { strCmd="leftArrow"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==39) { strCmd="rightArrow"; }
if(oE.ctrlKey==true && oE.altKey==false && oE.shiftKey==false && oE.keyCode==71) { strCmd="goto"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==81) { strCmd="q"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==87) { strCmd="w"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==65) { strCmd="leftArrow"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==83) { strCmd="rightArrow"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==69) { strCmd="e"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==68) { strCmd="d"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==80) { strCmd="p"; }
if(oE.ctrlKey==true && oE.altKey==false && oE.shiftKey==false && oE.keyCode==89) { strCmd="export"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==20) { strCmd="leftClick"; }
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==82) { strCmd="search4indexofsoundblock"; } // [r]
if(oE.ctrlKey==false && oE.altKey==false && oE.shiftKey==false && oE.keyCode==78) { strCmd="listenbyindex"; } // [n]
if(strCmd!="")
{
doCmd(strCmd);
}
}
function doCmd(strCmd)
{
console.log(strCmd);
if(faAudioData!=null && strCmd=="leftArrow")
{
var fMoving=(fTimeEnd-fTimeBegin)/20;
fTimeBegin-=fMoving;
fTimeEnd-=fMoving;
if(fTimeBegin<0)
{
fTimeEnd+=Math.abs(fTimeBegin);
fTimeBegin=0;
}
drawWave(faAudioData, "white", "black");
drawMarks();
}
else if(faAudioData!=null && strCmd=="rightArrow")
{
var fMoving=(fTimeEnd-fTimeBegin)/20;
fTimeBegin+=fMoving;
fTimeEnd+=fMoving;
if(fTimeEnd>oBufferForMp3.duration)
{
fTimeEnd=oBufferForMp3.duration;
fTimeBegin-=(fTimeEnd-oBufferForMp3.duration);
}
drawWave(faAudioData, "white", "black");
drawMarks();
}
else if(faAudioData!=null && strCmd=="+")
{
var fTimeSpan=(fTimeEnd-fTimeBegin)/4;
fTimeBegin+=fTimeSpan;
fTimeEnd-=fTimeSpan;
drawWave(faAudioData, "white", "black");
drawMarks();
}
else if(faAudioData!=null && strCmd=="-")
{
var fTimeCenter=(fTimeBegin+fTimeEnd)/2;
var fTimeSpan=(fTimeEnd-fTimeBegin)/2;
fTimeBegin-=fTimeSpan;
fTimeEnd+=fTimeSpan;
if(fTimeBegin<0) { fTimeBegin=0; }
if(fTimeEnd>oBufferForMp3.duration) { fTimeEnd=oBufferForMp3.duration; }
drawWave(faAudioData, "white", "black");
drawMarks();
}
else if(faAudioData!=null && strCmd=="goto")
{
var fTmpTimeBegin=null;
var fTmpTimeSpan=null;
while(true)
{
fTmpTimeBegin=prompt("請輸入起始秒數", fTimeBegin);
if(fTmpTimeBegin==null) { break; }
fTmpTimeSpan=prompt("請輸入秒數寬度", 4);
if(fTmpTimeSpan==null) { break; }
fTimeBegin=fTmpTimeBegin*1;
fTimeEnd=fTimeBegin*1+fTmpTimeSpan*1;
if(fTimeBegin<0) { fTimeBegin=0; }
if(fTimeEnd>oBufferForMp3.duration) { fTimeEnd=oBufferForMp3.duration; }
drawWave(faAudioData, "white", "black");
drawMarks();
break;
}
}
else if(faAudioData!=null && strCmd=="q")
{
startListening();
}
else if(faAudioData!=null && strCmd=="w")
{
stopListening();
}
else if(faAudioData!=null && strCmd=="e")
{
search4BlockAndPlay();
}
else if(faAudioData!=null && strCmd=="d")
{
search4BlockAndDelete();
}
else if(faAudioData!=null && strCmd=="p")
{
search4BlockAndSeparate();
}
else if(faAudioData!=null && strCmd=="`")
{
gebi('inpKeyboardCapture').checked=!gebi('inpKeyboardCapture').checked;
}
else if(faAudioData!=null && strCmd=="export")
{
rearrangeMarksAndExport();
}
else if(faAudioData!=null && strCmd=="leftClick")
{
capslock_simulate_leftclick();
}
else if(faAudioData!=null && strCmd=="autoMarking")
{
autoMarking();
}
else if(faAudioData!=null && strCmd=="search4indexofsoundblock")
{
search4BlockAndShow();
}
else if(faAudioData!=null && strCmd=="listenbyindex")
{
listenByIndex();
}
}
</script>
<script>
function drawMarks()
{
var oCanvas=gebi("myCanvas");
var oCtx=oCanvas.getContext("2d");
// 畫標記
var straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
for(var iMark=0; iMark<straMark.length; iMark+=2)
{
var fTimeLeft=straMark[iMark]*1;
var fLeftX=(fTimeLeft-fTimeBegin)/(fTimeEnd-fTimeBegin)*oCanvas.width;
if((iMark+1)<straMark.length)
{
// 兩個
var fTimeRight=straMark[iMark+1]*1;
var fRightX=(fTimeRight-fTimeBegin)/(fTimeEnd-fTimeBegin)*oCanvas.width;
oCtx.beginPath();
oCtx.strokeStyle="blue";
oCtx.lineWidth=2;
oCtx.strokeRect(fLeftX,oCanvas.height/10,fRightX-fLeftX,oCanvas.height/10*8);
oCtx.stroke();
}
else
{
// 一個
oCtx.beginPath();
oCtx.strokeStyle="green";
oCtx.lineWidth=2;
oCtx.moveTo(fLeftX,0);
oCtx.lineTo(fLeftX,oCanvas.height);
oCtx.stroke();
}
}
}
var fLastCursorX=0;
function mouseMoving(oEvent)
{
if(faAudioData==null) { return; }
var oCanvas=gebi("myCanvas");
var oCtx=oCanvas.getContext("2d");
var oBackupCanvas=gebi("myBackupCanvas");
var oBackupCtx=oCanvas.getContext("2d");
var fX = oEvent.clientX-(oCanvas.getBoundingClientRect()).left;
var fY = oEvent.clientY-(oCanvas.getBoundingClientRect()).top;
oCtx.beginPath();
oCtx.drawImage(oBackupCanvas,0,0); // 畫面還原
drawMarks();
oCtx.beginPath();
oCtx.strokeStyle="red";
oCtx.lineWidth=2;
oCtx.moveTo(fX,0);
oCtx.lineTo(fX,oCanvas.height);
oCtx.stroke();
oCtx.lineWidth=1;
fLastCursorX=fX;
var fTimeCurrent=Math.floor((fTimeBegin+fLastCursorX/oCanvas.width*(fTimeEnd-fTimeBegin))*10000)/10000;
gebi("divTimeCursor").innerHTML=fTimeCurrent;
}
function capslock_simulate_leftclick()
{
if(faAudioData==null) { return; }
var oCanvas=gebi("myCanvas");
var fX=fLastCursorX;
var fCurrentTime=fTimeBegin + fX/oCanvas.width*(fTimeEnd-fTimeBegin);
gebi("taMarks").value
=gebi("taMarks").value
+getStrFormattedSecs(fCurrentTime)+"\r\n";
}
function mouseClick(oEvent)
{
if(faAudioData==null) { return; }
var oCanvas=gebi("myCanvas");
var fX = oEvent.clientX-(oCanvas.getBoundingClientRect()).left;
var fY = oEvent.clientY-(oCanvas.getBoundingClientRect()).top;
var fCurrentTime=fTimeBegin + fX/oCanvas.width*(fTimeEnd-fTimeBegin);
gebi("taMarks").value
=gebi("taMarks").value
+getStrFormattedSecs(fCurrentTime)+"\r\n";
}
function mouseWheel(oEvent)
{
if(faAudioData==null) { return; }
oEvent.preventDefault();
if(oEvent.deltaY<0) { doCmd("+"); }
else { doCmd("-"); }
}
</script>
<script>
function rearrangeMarks()
{
var straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var oaBuffer=[];
var straFinal=[];
var fVal1, fVal2;
for(var iMark=0; iMark<straMark.length; iMark+=2)
{
if((iMark+1)<straMark.length)
{
fVal1=straMark[iMark]*1;
fVal2=straMark[iMark+1]*1;
if(fVal1<fVal2)
{
oaBuffer.push([fVal1,fVal2]);
}
else
{
oaBuffer.push([fVal2,fVal1]);
}
}
else
{
oaBuffer.push([straMark[iMark]*1]);
}
}
oaBuffer.sort(
function(ary1,ary2)
{
if(ary1[0]>ary2[0]) { return true; }
return false;
}
);
for(var i=0; i<oaBuffer.length; i++)
{
for(var j=0; j<oaBuffer[i].length; j++)
{
straFinal.push(oaBuffer[i][j]);
}
if(oaBuffer[i].length==1)
{
straFinal.push(oaBuffer[i][0]+0.0001);
}
}
gebi("taMarks").value=straFinal.join("\r\n")+"\r\n";
}
function rearrangeMarksAndExport()
{
rearrangeMarks();
var straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var straFinal=[];
for(var i=0; i<straMark.length; i+=2)
{
straFinal.push(straMark[i]+"\t"+straMark[i+1]);
}
gebi("taPlayground2").value=straFinal.join("\r\n");
}
</script>
<script>
function listenByIndex()
{
var straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var iTotalSoundBlocks=Math.floor(straMark.length/2);
if(faAudioData.length<1)
{
alert("抱歉,目前沒有載入聲音檔可供聆聽!");
return;
}
if(iTotalSoundBlocks<1)
{
alert("抱歉,目前沒有任何標記的區塊可供聆聽!");
return;
}
rearrangeMarks();
var iIdxOfSoundBlock=gebi("inpIdxOfSoundBlock").value*1;
if(iIdxOfSoundBlock<0) { iIdxOfSoundBlock=0; }
if(iIdxOfSoundBlock>(iTotalSoundBlocks-1)) { iIdxOfSoundBlock=(iTotalSoundBlocks-1); }
var fTime1=straMark[iIdxOfSoundBlock*2]*1;
var fTime2=straMark[iIdxOfSoundBlock*2+1]*1;
var fTimeWatch=fTime1-0.05;
var fTimeRange=4;
if((fTime2-fTimeWatch)>fTimeRange) { fTimeRange=(fTime2-fTimeWatch)*2; }
gotoThisSoundBlock(fTimeWatch,fTimeRange);
listenToThisRange(fTime1,fTime2);
gebi("spanListenByIndeStatus").innerHTML="聆聽 "+fTime1+" ~ "+fTime2;
// 移動到下一個區塊
iIdxOfSoundBlock=(iIdxOfSoundBlock+1) % iTotalSoundBlocks;
gebi("inpIdxOfSoundBlock").value=iIdxOfSoundBlock;
}
function gotoThisSoundBlock(fTime1,fTimeRange)
{
var fTmpTimeBegin=fTime1;
var fTmpTimeSpan=fTimeRange;
fTimeBegin=fTmpTimeBegin*1;
fTimeEnd=fTimeBegin*1+fTmpTimeSpan*1;
if(fTimeBegin<0) { fTimeBegin=0; }
if(fTimeEnd>oBufferForMp3.duration) { fTimeEnd=oBufferForMp3.duration; }
drawWave(faAudioData, "white", "black");
drawMarks();
}
function search4BlockAndShow()
{
rearrangeMarks();
var oCanvas=gebi("myCanvas");
var fTimeCurrent=fTimeBegin+fLastCursorX/oCanvas.width*(fTimeEnd-fTimeBegin);
straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var iMax=straMark.length-(straMark.length%2);
for(var i=0; i<iMax; i+=2)
{
var fTime1=straMark[i]*1;
var fTime2=straMark[i+1]*1;
var fTimeTmp;
if(fTime1>fTime2)
{
fTimeTmp=fTime1;
fTime1=fTime2;
fTime2=fTimeTmp;
}
if(fTimeCurrent>=fTime1 && fTimeCurrent<=fTime2)
{
gebi("inpIdxOfSoundBlock").value=Math.floor(i/2);
break;
}
}
}
</script>
<script>
var oAudioContext4Listen = new (window.AudioContext || window.webkitAudioContext); // 放在外面,一次就好!這樣資源配置才會順暢!
var oBufferSource4Listen=null;
function listenToThisRange(fTime1, fTime2)
{
if(faAudioData==null) { return; }
var fVolume=gebi("inpVolume").value*1;
try
{
stopListening();
var iIdx1=Math.floor(fTime1/oBufferForMp3.duration*(faAudioData.length-1));
var iIdx2=Math.floor(fTime2/oBufferForMp3.duration*(faAudioData.length-1));
var faAudioBuffer=faAudioData.slice(iIdx1,iIdx2+1);
var iBufferSize=faAudioBuffer.length;
var oBuffer=oAudioContext4Listen.createBuffer(1, iBufferSize, oBufferForMp3.sampleRate);
var oData=oBuffer.getChannelData(0);
for(var i=0; i<faAudioBuffer.length; i++)
{
oData[i]=faAudioBuffer[i]*fVolume;
if(oData[i]>1) { oData[i]=1; }
if(oData[i]<-1) { oData[i]=-1; }
}
oBufferSource4Listen=oAudioContext4Listen.createBufferSource();
oBufferSource4Listen.buffer=oBuffer;
oBufferSource4Listen.connect(oAudioContext4Listen.destination);
oBufferSource4Listen.start();
}
catch(oError)
{
alert("play error!"+oError);
}
}
function stopListening()
{
if(oBufferSource4Listen!=null)
{
oBufferSource4Listen.stop();
}
}
function startListening()
{
var stra1=gebi("inpListenRange").value.split(" ").join("").split(",");
var fTime1=null, fTime2=null;
if(stra1.length==2)
{
fTime1=stra1[0]*1;
fTime2=stra1[1]*1;
}
else
{
straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
if(straMark.length>1)
{
var iIdx=((straMark.length-(straMark.length%2))/2)*2-2;
fTime1=straMark[iIdx]*1;
fTime2=straMark[iIdx+1]*1;
}
}
if(fTime1==null || fTime2==null) { return; } // 沒有東西可以播放
listenToThisRange(fTime1,fTime2);
gebi("inpListenRange").value="";
}
function search4BlockAndPlay()
{
var oCanvas=gebi("myCanvas");
var fTimeCurrent=fTimeBegin+fLastCursorX/oCanvas.width*(fTimeEnd-fTimeBegin);
straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var iMax=straMark.length-(straMark.length%2);
for(var i=0; i<iMax; i+=2)
{
var fTime1=straMark[i]*1;
var fTime2=straMark[i+1]*1;
var fTimeTmp;
if(fTime1>fTime2)
{
fTimeTmp=fTime1;
fTime1=fTime2;
fTime2=fTimeTmp;
}
if(fTimeCurrent>=fTime1 && fTimeCurrent<=fTime2)
{
listenToThisRange(fTime1,fTime2);
break;
}
}
}
function search4BlockAndDelete()
{
rearrangeMarks();
var oCanvas=gebi("myCanvas");
var fTimeCurrent=fTimeBegin+fLastCursorX/oCanvas.width*(fTimeEnd-fTimeBegin);
straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var iMax=straMark.length-(straMark.length%2);
for(var i=0; i<iMax; i+=2)
{
var fTime1=straMark[i]*1;
var fTime2=straMark[i+1]*1;
if(fTimeCurrent>=fTime1 && fTimeCurrent<=fTime2)
{
straMark[i]='';
straMark[i+1]='';
}
}
gebi("taMarks").value=straMark.filter(Boolean).join("\r\n")+"\r\n";
}
function search4BlockAndSeparate_goFindMiddle(fTime1,fTime2)
{
var fTimeMiddle=(fTime1+fTime2)/2;
fAutoTimeSpan=gebi("inpAutoTimeSpan").value*1;
var iDataIdxBegin=Math.floor(fTime1/oBufferForMp3.duration*(faAudioData.length-1));
var iDataIdxEnd=Math.floor(fTime2/oBufferForMp3.duration*(faAudioData.length-1));
var iDataSpan=Math.floor(fAutoTimeSpan*oBufferForMp3.sampleRate);
var fTimeTrim=(fTime2-fTime1)/10;
var faSearchBlock=faAudioData.slice(iDataIdxBegin,iDataIdxEnd+1);
var fMax4All=Math.max(...faSearchBlock);
var fMin4All=Math.min(...faSearchBlock);
var oaMaxMin=[];
for(var iDataIdx=iDataIdxBegin; iDataIdx<=iDataIdxEnd; iDataIdx+=iDataSpan)
{
var faSearchBlockNow=faAudioData.slice(iDataIdx,iDataIdx+iDataSpan);
var fMax4Now=Math.max(...faSearchBlockNow);
var fMin4Now=Math.min(...faSearchBlockNow);
var fTimeMiddleNow=(iDataIdx+iDataSpan/2)/(faAudioData.length-1)*oBufferForMp3.duration;
var fScore=(fMax4Now-fMin4Now)/(fMax4All-fMin4All);
if((fMax4Now-fMin4Now)<((fMax4All-fMin4All)/2))
{
if(fTimeMiddleNow>=(fTime1+fTimeTrim) && fTimeMiddleNow<=(fTime2-fTimeTrim))
{
oaMaxMin.push({ fScore:fScore, fMaxMin4Now:(fMax4Now-fMin4Now), fTimeMiddleNow:fTimeMiddleNow });
}
}
}
oaMaxMin.sort(
function(a,b)
{
if(a.fScore>b.fScore) { return 1; }
else if(a.fScore<b.fScore) { return -1; }
else { return 0; }
}
);
console.log(oaMaxMin); // nmomtf
if(oaMaxMin.length>0)
{
fTimeMiddle=oaMaxMin[0].fTimeMiddleNow;
}
return fTimeMiddle;
}
function search4BlockAndSeparate()
{
rearrangeMarks();
var oCanvas=gebi("myCanvas");
var fTimeCurrent=fTimeBegin+fLastCursorX/oCanvas.width*(fTimeEnd-fTimeBegin);
straMark=gebi("taMarks").value.split(" ").join("").split("\r\n").join("\n").split("\r").join("\n").split("\n").filter(Boolean);
var iMax=straMark.length-(straMark.length%2);
for(var i=0; i<iMax; i+=2)
{
var fTime1=straMark[i]*1;
var fTime2=straMark[i+1]*1;
if(fTimeCurrent>=fTime1 && fTimeCurrent<=fTime2)
{
var fTimeMiddle=search4BlockAndSeparate_goFindMiddle(fTime1,fTime2);
straMark[i]=fTime1+"\r\n"+(fTimeMiddle-0.02);
straMark[i+1]=(fTimeMiddle+0.02)+"\r\n"+fTime2;
}
}
gebi("taMarks").value=straMark.filter(Boolean).join("\r\n")+"\r\n";
}
</script>
<script>
function load_from_www()
{
// 要從 https 的伺服器載入
const request = new XMLHttpRequest();
request.open("GET", "total.mp3");
request.responseType = "arraybuffer";
request.onload = function()
{
console.log('onload');
let undecodedAudio = request.response;
oAudioContext.decodeAudioData(
undecodedAudio,
(data) =>
{
oBuffer=data;
console.log(oBuffer.getChannelData(0));
});
};
request.send();
console.log('send');
}
</script>
<script>
var fAutoTimeBegin=0;
var fAutoTimeEnd=100;
var fAutoTimeSpan=0.01; // 每 0.01 秒,一個區塊
var fAutoThreshold=0.02; // -1 ~ +1 的振幅
var fAutoTimeBlockMinSecs=0.2;
function autoMarking()
{
fAutoTimeBegin=gebi("inpAutoTimeBegin").value*1;
fAutoTimeEnd=gebi("inpAutoTimeEnd").value*1;
fAutoTimeSpan=gebi("inpAutoTimeSpan").value*1;
fAutoThreshold=gebi("inpAutoThreshold").value*1;
fAutoTimeBlockMinSecs=gebi("inpAutoTimeBlockMinSecs").value*1;
var iAutoDataIdxBegin=Math.floor(fAutoTimeBegin/oBufferForMp3.duration*(faAudioData.length-1));
var iAutoDataIdxEnd=Math.floor(fAutoTimeEnd/oBufferForMp3.duration*(faAudioData.length-1));
var iAutoDataSpan=Math.floor(fAutoTimeSpan*oBufferForMp3.sampleRate);
if(iAutoDataIdxBegin<0) { iAutoDataIdxBegin=0; }
if(iAutoDataIdxEnd<0) { iAutoDataIdxEnd=0; }
if(iAutoDataIdxBegin>(faAudioData.length-1)) { iAutoDataIdxBegin=(faAudioData.length-1); }
if(iAutoDataIdxEnd>(faAudioData.length-1)) { iAutoDataIdxEnd=(faAudioData.length-1); }
if(iAutoDataSpan<1) { iAutoDataSpan=1; }
var oAB1=null, oAB2=null;
var iAutoDataFoundBegin=null, iAutoDataFoundEnd=null;
var iAutoDataIdx=iAutoDataIdxBegin;
while(iAutoDataIdx<=iAutoDataIdxEnd)
{
iAutoDataFoundBegin=null;
iAutoDataFoundEnd=null;
oAB1=getOAutoBlock(iAutoDataIdx,iAutoDataIdx+iAutoDataSpan);
if(oAB1.booHasSound==true)
{
iAutoDataFoundBegin=iAutoDataIdx;
iAutoDataFoundEnd=iAutoDataIdx+iAutoDataSpan-1;
iAutoDataIdx+=iAutoDataSpan;
while(iAutoDataIdx<=iAutoDataIdxEnd)
{
oAB2=getOAutoBlock(iAutoDataIdx,iAutoDataIdx+iAutoDataSpan);
if(oAB2.booHasSound==true) { iAutoDataFoundEnd=iAutoDataIdx+iAutoDataSpan-1; }
else { break; }
iAutoDataIdx+=iAutoDataSpan;
}
if(iAutoDataFoundEnd>iAutoDataIdxEnd) { iAutoDataFoundEnd=iAutoDataIdxEnd; }
var fAutoTimeFoundBegin=iAutoDataFoundBegin/(faAudioData.length-1)*oBufferForMp3.duration;
var fAutoTimeFoundEnd=iAutoDataFoundEnd/(faAudioData.length-1)*oBufferForMp3.duration;
if((fAutoTimeFoundEnd-fAutoTimeFoundBegin)>=fAutoTimeBlockMinSecs)
{
gebi("taMarks").value
=gebi("taMarks").value
+getStrFormattedSecs(fAutoTimeFoundBegin)+"\r\n"
+getStrFormattedSecs(fAutoTimeFoundEnd)+"\r\n";
}
}
iAutoDataIdx+=iAutoDataSpan;
}
}
function getOAutoBlock(i1,i2)
{
var faAutoBlock=faAudioData.slice(i1,i2);
var fAutoMinVal=Math.min(...faAutoBlock);
var fAutoMaxVal=Math.max(...faAutoBlock);
var booHasSound=(fAutoMaxVal-fAutoMinVal)>fAutoThreshold;
if(i1>=faAudioData.length)
{
faAutoBlock=[];
fAutoMinVal=0;
fAutoMaxVal=0;
booHasSound=false;
}
return { faAutoBlock:faAutoBlock, fAutoMinVal:fAutoMinVal, fAutoMaxVal:fAutoMaxVal, booHasSound:booHasSound };
}
</script>
</body>
</html>