UNPKG

formantanalyzer

Version:

Extract formant features such as frequency, power, energy, and bandwidth of formants at syllable or word level from audio sources in a web browser using WebAudio API.

330 lines (245 loc) 11.7 kB
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=0"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="assets/css/w3.css"> <link rel="stylesheet" href="assets/css/w3-theme-black.css"> <link rel="stylesheet" href="assets/css/main.css"> <title>Formant Analyzer</title> </head> <body class="w3-black"> <div class="topnav"> <a href="https://github.com/tabahi/formantanalyzer.js">Github</a> </div> <div class="w3-container w3-center w3-opacity"> <h5 id="app_title">Formant Analyzer</h5> </div> <div class="w3-container w3-bottombar w3-border-dark-gray"> <!-- Main body container --> <div class="w3-row w3-center w3-animate-zoom" id="canvas_div"> <canvas id="SpectrumCanvas" width="1200" height="300" ></canvas> </div> <div class="w3-row w3-center"> <p id="msg">Press Mic</p> </div> <div class="w3-row w3-center w3-padding-small"> <!-- Buttons --> <div class="w3-col m6"> <div id="dropZone" class="w3-card-4"> <label for="filesx" class="w3-hover-text-amber">Load Audio: </label> <input type="file" id="filesx" name="filesx[]" onchange="readmultifiles(this.files)" multiple="" class="w3-button w3-hover-border-khaki w3-padding" placeholder="Audio files" accept="audio/*" /> <input type="button" id="play_button" value="Play" class="w3-button w3-wide w3-border w3-border-light-blue w3-padding"> </div> </div> <div class="w3-col m2"> <input type="button" id="mic_button" value="Mic" class="w3-button w3-wide w3-border w3-border-amber w3-padding"> </div> <div class="w3-col m4"> <select class="w3-select w3-dark-input" id="output_level" style="width:95%"> <option value="0" disabled>Output Level</option> <option value="1">Bars</option> <option value="2" selected>Spectrum</option> <option value="3">Segments</option> <option value="4">Segment Formants</option> <option value="5">Segment Features [ML]</option> <option value="10">Syllable Formants </option> <option value="11">Distributions 264x [ML]</option> <option value="12">Syllable Curves 23x [ML]</option> <option value="13">Syllable 53x [ML]</option> </select> </div> </div> <div class="w3-row w3-center w3-padding-small"> <!-- File loading zone --> </div> </div> <!-- Main body container ends --> <div class="footer"> <a href="https://github.com/tabahi/formantanalyzer.js/blob/main/index.html"> <span>Formant Analyzer bare minium starter file</span> </a> </div> <script src="https://unpkg.com/formantanalyzer@1.1.8/index.js"></script> <script> var play_status = { playing:false, files_processed:0, current_file_index:0, Source:1 }; var loaded_files = null; function Configure_FormantAnalyzer() { const BOX_HEIGHT = 300; const BOX_WIDTH = window.screen.availWidth - 50; //reset the size of canvas element using the screen width document.getElementById('SpectrumCanvas').width = BOX_WIDTH; document.getElementById('SpectrumCanvas').height = BOX_HEIGHT; let launch_config = { plot_enable: true, spec_type: 1, output_level: document.getElementById('output_level').value, plot_len: 200, f_min: 50, f_max: 4000, N_fft_bins: 256, N_mel_bins: 128, window_width: 25, window_step: 15, pause_length: 200, min_seg_length: 500, auto_noise_gate: true, voiced_min_dB: 10, voiced_max_dB: 100, plot_lag: 1, pre_norm_gain: 1000, high_f_emph: 0.0, plot_canvas: document.querySelector('#SpectrumCanvas').getContext('2d'), canvas_width: BOX_WIDTH, canvas_height: BOX_HEIGHT }; FormantAnalyzer.configure(launch_config); } /*This function is called asynchronously after each segment or syllable depending on the output level. si is the index of segment/syllable out of all since reset*/ async function call_backed(seg_index, seg_label, seg_time, extracted_features) { console.log(seg_index, seg_label, seg_time); //console.log(extracted_features); return; } document.querySelector('#mic_button').addEventListener('click', function(e) { e.preventDefault(); play_status.Source = 3; if(play_status.playing == false) { document.getElementById('msg').textContent = "Streaming from mic..."; disable_buttons(true); document.getElementById("mic_button").value = "..."; document.getElementById("mic_button").value = "Stop"; document.getElementById("mic_button").disabled = false; play_status.playing = true; Configure_FormantAnalyzer(); FormantAnalyzer.LaunchAudioNodes(3, null, call_backed, ['mic'], false, false, null, null).then(function() { stop_playing("End sop play"); }).catch((err)=>{ console.log(err); stop_playing("Error during play start"); document.getElementById('msg').textContent = "Error streaming"; }); } else { stop_playing("Button pressed"); document.getElementById('msg').textContent = "Stopped"; } }); document.querySelector('#play_button').addEventListener('click', function(e) { e.preventDefault(); play_status.Source = 1; if(play_status.playing) { stop_playing("Button pressed"); document.getElementById('msg').textContent = "Stopped"; } else { if(!loaded_files) { document.getElementById('msg').textContent = "No files selected to play"; } else { disable_buttons(true); play_status.current_file_index = 0; play_status.files_processed = 0; if(PlayNextFile()) { play_status.playing = true; document.getElementById("play_button").value = "Stop"; document.getElementById("play_button").disabled = false; document.getElementById('msg').textContent = "Playing"; } } } }); function PlayNextFile() //play the files loaded in drop zone queue { //This function plays all the loaded files. After each file it calls 'finished_file_play()', which recursively calls this function again until it reaches the end if(play_status.current_file_index >= loaded_files.length) return false; let reader = new FileReader(); //let this_file = loaded_files[play_status.current_file_index]; reader.onload = function(e) { let this_file_name = loaded_files[play_status.current_file_index].name; let mimeType= loaded_files[play_status.current_file_index].type; if(mimeType.includes("audio/") || mimeType.includes("video/")) { //let this_file_bin = e.target.result; document.getElementById('msg').textContent = "Playing file " + String(play_status.current_file_index+1) + "/" + String(loaded_files.length) + ', ' + this_file_name; Configure_FormantAnalyzer(); FormantAnalyzer.LaunchAudioNodes(1, e.target.result, call_backed, [this_file_name], false, false, null, null).then(function() { e = null; //clear memory reader = null; stop_playing("Finished processing all files"); document.getElementById('msg').textContent = "Finished playing " + String(play_status.files_processed) + " files"; return true; }).catch((err)=>{ console.log(err); e = null; //clear memory reader = null; stop_playing("Error during play start"); document.getElementById('msg').textContent = "Error playing: " + String(this_file_name); return false; }); } else { document.getElementById('msg').textContent = "Invalid format: " + String(this_file_name) + ", " + String(mimeType); return false; } } reader.readAsArrayBuffer(loaded_files[play_status.current_file_index]); //read binary of audio file return true; } function stop_playing(reason) { play_status.playing = false; FormantAnalyzer.StopAudioNodes(reason); document.getElementById("play_button").value = "Play"; document.getElementById("mic_button").value = "Mic"; document.getElementById("play_button").disabled = false; document.getElementById("mic_button").disabled = false; } function disable_buttons(disable) { document.getElementById("play_button").disabled = disable; document.getElementById("mic_button").disabled = disable; } /* Unrelated File loading functions */ function readmultifiles(files) { if(play_status.playing) stop_playing("New file loaded"); loaded_files = null; play_status.current_file_index = 0; play_status.files_processed = 0; loaded_files = files; document.getElementById('msg').textContent = "Total " + String(loaded_files.length) + " files loaded"; } var handleDragOver = function(e) { e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = 'copy'; } var handleDrop = function(e) { e.preventDefault() e.stopPropagation() readmultifiles(e.dataTransfer.files); } function init_drag_and_drop() //initialize drag and drop zone elements { if (window.File && window.FileReader && window.FileList && window.Blob) { //All the File APIs are supported. } else { console.warn('The Drag and Drop File APIs are not fully supported in this browser.'); } const dropzone_div = document.getElementById('dropZone'); dropzone_div.addEventListener('drop', handleDrop, false); dropzone_div.addEventListener('dragover', handleDragOver, false); } init_drag_and_drop(); </script> </body> </html>