<html> <head>
<title>Calculate Your Vocal Tract Length</title> <script> var audio_context; var recorder; window.onload = function init() { try { // webkit shim window.AudioContext = window.AudioContext || window.webkitAudioContext; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia; window.URL = window.URL || window.webkitURL; audio_context = new AudioContext; navigator.getUserMedia({audio: true}, startUserMedia, function(e) { console.log('No live audio input: ' + e); }); } catch (e) { alert('No web audio support in this browser!'); } }; function startUserMedia(stream) { var input = audio_context.createMediaStreamSource(stream); recorder = new Recorder(input); document.querySelector("[data-id=allowHint]").style.display = 'none'; startRecordingButton = document.querySelector("[data-action=startRecording]"); startRecordingButton.disabled = false; startRecordingButton.addEventListener("click", startRecording, false); } function startRecording() { this.innerHTML = "Recording... " this.disabled = true; recorder && recorder.record(); setTimeout(stopRecording, 2000, this); } function stopRecording(button) { recorder && recorder.stop(); document.querySelector("[data-action=startRecording]").innerHTML = "Calculating..."; recorder.exportWAV(sendWAV); } function calculateVocalTractLength(formant1) { return 34400 / (4.0 * formant1); } function roundTo(number, places) { return Math.round(number * Math.pow(10, places))/Math.pow(10, places); } function setFormant1Result(formant1Text) { formant1 = parseFloat(formant1Text); document.querySelector("[data-action=startRecording]").disabled = false; document.querySelector("[data-action=startRecording]").innerHTML = "Record"; document.querySelector("[data-id=results]").style.display = 'initial'; document.querySelector("[data-id=formant1]").innerHTML = roundTo(formant1, 0); document.querySelector("[data-id=vocalTractLength]").innerHTML = roundTo(calculateVocalTractLength(formant1), 1); } function sendWAV(blob) { var formData = new FormData(); formData.append('data', blob); xhr = new XMLHttpRequest(); xhr.open("POST", "extract_formant1", true); xhr.onload = function() { setFormant1Result(xhr.responseText); } xhr.send(formData); recorder.clear(); } </script> <script src="recorder.js"></script>
</head> <body>
<nav> <a href="/">Home</a> <a href="https://github.com/mxhold/vocal_tract_length">Source code</a> </nav> <section> <h1>Calculate Your Vocal Tract Length</h1> <p> Click <em>Record</em> and produce an unobstructed vowel (the last sound in the English word <em>comma</em>) continuously for 2 seconds. </p> <p data-id="allowHint"> <em>First, click "Allow" in your browser to let this page to record your voice.</em> </p> <p> <button data-action="startRecording" disabled>Record</button> </p> </section> <section data-id="results" style="display: none;"> <h1>Results</h1> <p> F<sub>1</sub> = <span data-id="formant1">error</span> Hz </p> <p> Estimated vocal tract length: <span data-id="vocalTractLength">error</span> cm </p> <p> Average vocal tract length (female): 14.1 cm<br> Average vocal tract length (male): 17 cm<br> </p> </section> <section> <h1>About</h1> <p> If we simplify things significantly, the human vocal tract is really just a tube that is closed on one end (where your vocal folds vibrate) and open on another end (your lips). </p> <p> Given this model, there is a formula that expresses the relationship between resonance frequencies (<em>f</em>, which we is what we are measuring here), the speed of sound (<em>v</em>, which we know), and the length of the tube (<em>L</em>, which is what we are trying to figure out): </p> <img src="frequency.png" alt="f = nv/2L" height="50"> <p> The <em>n</em> here refers to which resonance frequency we're talking about. Vowels will usually have four or more distinguishable resonance frequencies, but we're just dealing with the first (F<sub>1</sub>) here, so we can replace this with 1. We can also replace <em>v</em> with 34400 cm/s since we know that's the speed of sound at sea level at room temperature (20°C). And since we know the frequency but want to determine the length, we can solve for L to get this equation: </p> <img src="length.png" alt="L = 34400/4f" height="50"> <h2>How are you measuring F<sub>1</sub>?</h2> <p> When you hit record, we are using a Javascript library that wraps the browser's audio recording API to record a 2 second WAV audio clip. Thanks to <a href="http://matt-diamond.com/">Matt Diamond's</a> <a href="https://github.com/mattdiamond/Recorderjs">Recorderjs project</a> for making that library. </p> <p> This data is then sent to the server where it is processed using <a href="http://praat.org">Praat</a>, a seriously cool program that can do all sorts of phonetic analysis. This gives us the first formant which we then plug into the above equation. </p> <p> You can see the source code for this page and the server side Praat script on <a href="https://github.com/mxhold/vocal_tract_length">GitHub</a>. </p> <h2>Is this accurate? Why is the estimate so inconsistent?</h2> <p> It is pretty hard to get people to produce an unobstructed vowel consistently so that is probably the biggest limitation with this method. We're also using a simplified model of the vocal tract, likely using a less than perfect microphone, and only taking a 2 second sample. </p> <p> The point of this is not to actually give you an accurate measure, but rather to show it's possible to calculate the length of the vocal tract from a recording, which I think is pretty cool. </p> </section>
</body> </html>