![]() |
<!DOCTYPE HTML> <HTML LANG="ja"> <HEAD> <META CHARSET="Shift_JIS"> <TITLE>ボイス レコーダー</TITLE> </HEAD> <BODY STYLE="background-color:#CCFFFF"> <DIV ALIGN=CENTER> <BR> <B>ボイス レコーダー</B> <BR><BR> <FORM> <TABLE><TR><TD NOWRAP> <INPUT TYPE=BUTTON ID="new" VALUE="録音" onClick="record(false)"> <INPUT TYPE=BUTTON ID="app" VALUE="追加録音" DISABLED onClick="record(true)" STYLE="margin-left:4px"><BR><BR> <INPUT TYPE=BUTTON ID="ply" VALUE="再生" DISABLED onClick="play()"> <SPAN STYLE="margin-left:1em">音量</SPAN><INPUT TYPE=RANGE ID="vol" MIN="0" MAX="1" STEP="any" VALUE="0.5" onInput="set_vol()"><BR><BR> <SPAN ID="ela"></SPAN><SPAN ID="len"></SPAN><BR> <INPUT TYPE=BUTTON ID="stp" VALUE="停止" DISABLED onClick="stop()"><BR><BR> <INPUT TYPE=BUTTON ID="sav" VALUE="保存..." DISABLED onClick="save()"><BR><BR> <INPUT TYPE=BUTTON ID="lod" VALUE="読込..." onClick="load()"> <SPAN ID="nam" STYLE="white-space:pre; display:inline-block; min-width:20em"></SPAN> </TD></TR></TABLE> </FORM> </DIV> <A ID="dl" STYLE="display:none"></A><!--保存処理用--> <SCRIPT TYPE="text/javascript"> <!-- // 録音/追加録音 function record(append) { window.append = append; elem_new.disabled = elem_append.disabled = elem_play.disabled = elem_save.disabled = elem_load.disabled = true; if(navigator.mozGetUserMedia == undefined) navigator.getUserMedia({video:false, audio:true}, success, error); else navigator.mozGetUserMedia({video:false, audio:true}, success, error); } function success(stream) { elem_stop.disabled = false; mode = true; // リサンプリング用オフライン コンテキスト作成 create_rec_off(); if(append) { // 追加録音 var data_len = data.length; if(data_len) { // データあり frac_len = data[data_len - 1].length; elapsed = (data_len - 1) * 8000 + frac_len; var sec = data_len; if(frac_len == 8000) { // 1秒未満の半端なし append = false; } else { sec--; var i = Math.ceil(frac_len * sample_rate / 8000); while(i_rec_off < i) rec_off_pcm[i_rec_off++] = 0; } show_elapsed(sec); } else { elapsed = 0; elem_elapsed.textContent = "0:00"; append = false; } } else { data = []; elapsed = 0; elem_elapsed.textContent = "0:00"; } elem_length.textContent = ""; elem_name.textContent = ""; rec_str = aud_ctx.createMediaStreamSource(stream); rec_str.connect(rec_scr); rec_scr.onaudioprocess = rec_proc; } function error(err) { alert("マイクが使用できません"); elem_new.disabled = elem_load.disabled = false; if(data != undefined) elem_append.disabled = elem_play.disabled = elem_save.disabled = false; } // 録音用プロセッサ function rec_proc(e) { var pcm = e.inputBuffer.getChannelData(0); for(var i_pcm = 0; i_pcm < 2048; i_pcm++) { if(i_rec_off == sample_rate) { // リサンプリング起動 with(rec_off_ctx.createBufferSource()) { buffer = rec_off_buf; connect(rec_off_ctx.destination); start(); } rec_off_ctx.startRendering(); // リサンプリング用オフライン コンテキスト作成 create_rec_off(); } rec_off_pcm[i_rec_off++] = pcm[i_pcm]; } } // 録音停止用プロセッサ function stop_proc(e) { rec_scr.onaudioprocess = null; rec_str.disconnect(); rec_str = undefined; rec_len = Math.floor(i_rec_off * 8000 / sample_rate); if(!rec_len) { // 1秒未満の半端なし setTimeout(rec_stop, 50); return; } while(i_rec_off < sample_rate) rec_off_pcm[i_rec_off++] = 0; // リサンプリング起動 with(rec_off_ctx.createBufferSource()) { buffer = rec_off_buf; connect(rec_off_ctx.destination); start(); } rec_off_ctx.startRendering(); } // 録音時リサンプリング用オフライン コンテキスト作成 function create_rec_off() { // サポートされることが保障されている sampleRate の最低値は,W3C の // ドキュメントでは 8192,Mozilla のドキュメントでは 22050 となっている. // Firefox 25 では 8000 も指定可能. with(rec_off_ctx = new OfflineAudioContext(1, 8000, 8000)) { oncomplete = rec_off_comp; rec_off_pcm = (rec_off_buf = createBuffer(1, sample_rate, sample_rate)).getChannelData(0); i_rec_off = 0; } } // 録音時リサンプリング完了 function rec_off_comp(e) { var pcm = e.renderedBuffer.getChannelData(0); if(rec_scr.onaudioprocess == null) // 録音終了 setTimeout(rec_stop, 50); else rec_len = 8000; var comp = new Uint8Array(rec_len); var i_pcm = 0; if(append) { // 追加 // 1秒未満の半端をコピー var old_comp = data.pop(); for(; i_pcm < frac_len; i_pcm++) comp[i_pcm] = old_comp[i_pcm]; append = false; } // エンコード for(; i_pcm < rec_len; i_pcm++) { var val = pcm[i_pcm]; if (val >= 1) { val = 0xff; } else if(val <= -1) { val = 0x7f; } else { var sign = (val >= 0); val = Math.floor(2047 * ((sign) ? val : - val)); if(val & 0x7e0) { // > 31 var exp = 1; while(val & 0x7e0) { val >>= 1; exp++; } val = (exp << 4) | val & 0xf; } if(sign) // プラス val |= 0x80; } comp[i_pcm] = val ^ 0x55; } data.push(comp); elapsed += rec_len; show_elapsed(Math.floor(elapsed / 8000)); } function rec_stop() { show_length(Math.round(elapsed / 8000)); stop_2(); } // 再生 function play() { i_data = 0; // PCM 作成 data_pcm(); if(pcm_ready < 0) // データなし return; elem_new.disabled = elem_append.disabled = elem_play.disabled = elem_save.disabled = elem_load.disabled = true; elem_stop.disabled = false; mode = false; play_pcm = new Float32Array(0); // ダミー i_play = 0; elapsed = 0; } // 再生用プロセッサ function play_proc(e) { var pcm = e.outputBuffer.getChannelData(0); var i_pcm; for(i_pcm = 0; i_pcm < 2048; i_pcm++) { if(i_play == play_pcm.length) { if(pcm_ready != 1) { // データ終了/PCM 作成未完了 if(pcm_ready < 0) { // データ終了 play_scr.onaudioprocess = null; play_scr.disconnect(); setTimeout(stop_2, 50); } for(var i = i_pcm; i < 2048; i++) pcm[i] = 0; break; } play_pcm = play_pcm_next; i_play = 0; // PCM 作成 data_pcm(); } pcm[i_pcm] = play_pcm[i_play++]; } elapsed += i_pcm; show_elapsed(Math.floor(elapsed / sample_rate)); } // 再生用 PCM 作成 function data_pcm() { if(i_data == data.length) { // データ終了 pcm_ready = -1; return; } pcm_ready = 0; var comp = data[i_data++]; var len = comp.length; // リサンプリング用オフライン コンテキスト作成 with(new OfflineAudioContext(1, Math.ceil(len * sample_rate / 8000), sample_rate)) { oncomplete = play_off_comp; var buff = createBuffer(1, len, 8000); var pcm = buff.getChannelData(0); for(var i_pcm = 0; i_pcm < len; i_pcm++) { // デコード var val = comp[i_pcm] ^ 0x55; var exp = (val & 0x70) >> 4; var val2 = ((val & 0x1f) << 1) | 0x1; if(exp > 1) val2 = (0x20 | val2) << (exp - 1); val2 /= 4095; pcm[i_pcm] = (val & 0x80) ? val2 : - val2; } // リサンプリング起動 var src = createBufferSource(); src.buffer = buff; src.connect(destination); src.start(); startRendering(); } } // 再生時リサンプリング完了 function play_off_comp(e) { play_pcm_next = new Float32Array(e.renderedBuffer.getChannelData(0)); pcm_ready = 1; if(i_data == 1) { // 初回 // 再生開始 play_scr.connect(gain_node); play_scr.onaudioprocess = play_proc; } } function set_vol() { gain_node.gain.value = Number(elem_volume.value); } // 停止 function stop() { if(mode) { // 録音中 rec_scr.onaudioprocess = stop_proc; } else { // 再生中 play_scr.onaudioprocess = null; play_scr.disconnect(); stop_2(); } } function stop_2() { elem_elapsed.textContent = "0:00"; elem_new.disabled = elem_append.disabled = elem_play.disabled = elem_save.disabled = elem_load.disabled = false; elem_stop.disabled = true; } // 経過時間表示 function show_elapsed(sec) { elem_elapsed.textContent = String(Math.floor(sec / 60)) + ":" + ("0" + String(sec % 60)).substr(-2); } // 録音長表示 function show_length(sec) { elem_length.textContent = "/" + String(Math.floor(sec / 60)) + ":" + ("0" + String(sec % 60)).substr(-2); } // 保存 function save() { with(document.getElementById("dl")) { if(download == undefined) return; download = String(Math.floor(Date.now() / 1000)) + ".dat"; if(href != "") URL.revokeObjectURL(href); href = URL.createObjectURL(new Blob(data)); click(); } } // 読み込み function load() { with(elem_file = document.createElement("INPUT")) { type = "FILE"; onchange = file_sel; click(); } } function file_sel() { var file = elem_file.files.item(0); elem_name.textContent = file.name; file_reader = new FileReader(); file_reader.onload = file_sel2; file_reader.readAsArrayBuffer(file); elem_new.disabled = elem_append.disabled = elem_play.disabled = elem_save.disabled = elem_load.disabled = true; } function file_sel2() { var buff = file_reader.result; file_reader = undefined; var len = buff.byteLength; data = []; var sec; if(len) { // データあり // 1秒ずつに分ける for(var off = 0; ; off += 8000) { if(len <= 8000) { data.push(new Uint8Array(buff, off)); break; } data.push(new Uint8Array(buff, off, 8000)); len -= 8000; } sec = data.length; if(len < 4000) // 0.5秒未満 sec--; } else { sec = 0; } elem_elapsed.textContent = "0:00"; show_length(sec); elem_new.disabled = elem_append.disabled = elem_play.disabled = elem_save.disabled = elem_load.disabled = false; } with(aud_ctx = new AudioContext()) { gain_node = createGain(); gain_node.gain.value = 0.5; gain_node.connect(destination); rec_scr = createScriptProcessor(2048, 1, 0); play_scr = createScriptProcessor(2048, 0, 1); sample_rate = sampleRate; } elem_new = document.getElementById("new"); elem_append = document.getElementById("app"); elem_play = document.getElementById("ply"); elem_volume = document.getElementById("vol"); elem_elapsed = document.getElementById("ela"); elem_length = document.getElementById("len"); elem_stop = document.getElementById("stp"); elem_save = document.getElementById("sav"); elem_load = document.getElementById("lod"); elem_name = document.getElementById("nam"); data = undefined; // ページを再ロードしたときのため document.forms[0].reset(); elem_append.disabled = elem_play.disabled = elem_stop.disabled = elem_save.disabled = true; elem_new.disabled = elem_load.disabled = false; //--> </SCRIPT> </BODY> </HTML> |