Pitch volume Detection in Speech Recognition – JavaScript

Web Speech API allows recording human voice and convert it to text or generate an audio file.

This can be used to enable the users to access the website hand-free and give commands with voice.

Sometimes, requires to detect the user voice volume.

To do this I am using volume-meter.js library.

It returns pitch volume.

Use this value to draw a graphical representation.

Pitch volume Detection in Speech Recognition - JavaScript


Contents

  1. Download volume-meter.js
  2. HTML
  3. Script
  4. Demo
  5. Conclusion

1. Download volume-meter.js

  • Download volume-meter.js from GitHub and copy it into your project directory.
  • Also, download the jQuery library.

2. HTML

Create index.html file.

Created 2 buttons –

  1. To start voice recognition.
  2. To stop voice recognition.

Display recording status in the <span id=‘status’> whether it is started or stopped.

Use <div id=‘voiceVolume’> to display the user voice pitch in the horizontal bar.

Display said words in the <textarea id='saidwords'>.

Included volume-meter.js and the jQuery library.

Completed Code

<style type="text/css">
.recorder{
  border: 1px solid black;
  width: 320px;
  height: 100px;
  text-align: center;
  padding-top: 50px;
}
#saidwords{
  width: 320px;
  height: 100px;
}
#volumeBar{
  width:200px;
  height: 10px;
  border: 1px solid black;
  position: relative;
  margin: 0 auto;
}
#voiceVolume{
  width: 0px;
  height: 10px;
  background: green;
}
</style>

<div class='recorder'>
   <input type="button" id="but_start" value='Start Recording' onclick='startSpeech();'>
   <input type="button" id="but_stop" value='Stop Recording' onclick='stopSpeech();'>

   <br><br>
   <span id='status'>Recording not started.</span>

   <!-- Volume bar -->
   <div id='volumeBar'><div id='voiceVolume' ></div> </div>

</div>

<br>
Said Words -<br>
<textarea id='saidwords'></textarea>

<!-- Script -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type='text/javascript' src='volume-meter.js'></script>

3. Script

meter variable is used to create an instance of the volume meter.

Create an object of webkitSpeechRecognition() and to detect voice continuously set recognition.continuous = true.

recognition.onresult is called when service returns a result. Display said words in the <textarea id='saidwords'>.

Start Recording –

When Start Recording button gets clicked then call startSpeech() function from where display “Recording Started.” Message in the <span id=‘status’>, clear the <textarea>, and assign true to recordingStarted.

Call recognition.start() to enable voice detection.

Stop Recording –

When Stop Recording button gets clicked then call stopSpeech() function from where display “Recording Stopped.” Message in the <span id=‘status’>, and assign false to recordingStarted.

Call recognition.stop() to stop voice detection.

Create voice pitch –

To detect stream define navigator.getuserMedia from where call startUserMedia() function.

In startUserMedia() function creates an analyzer and connects the stream source.

Assign createAudioMeter(ctx) to meter and connect with streamNode.

Call drawLoop() function to create a pitch volume meter.

Calculate width –

Get the voice pitch volume and set minimum_volume = 130. You can change the value if you want.

If recording starts then find the percentage and assign to the width variable.

Update the <div id=‘voiceVolume’ > width.

Completed Code

var meter = null;
var WIDTH = 500;
var recordingStarted = false;

// initialize SpeechRecognition object
let recognition = new webkitSpeechRecognition();
recognition.maxAlternatives = 1;
recognition.continuous = true;

// Detect the said words
recognition.onresult = e => {

  var current = event.resultIndex;

  // Get a transcript of what was said.
  var transcript = event.results[current][0].transcript;

  // Add the current transcript with existing said values
  var noteContent = $('#saidwords').val();
  noteContent += ' ' + transcript;
  $('#saidwords').val(noteContent);

}

// Stop recording
function stopSpeech(){

  // Change status
  $('#status').text('Recording Stopped.');
  recordingStarted = false;

  // Stop recognition
  recognition.stop();
}

// Start recording
function startSpeech(){
  try{ // calling it twice will throw..
    $('#status').text('Recording Started.'); 
    $('#saidwords').val('');
    recordingStarted = true;

    // Start recognition
    recognition.start();
  }
  catch(e){}
}

navigator.getUserMedia({audio: true}, startUserMedia, function(e) {
  __log('No live audio input: ' + e);
});

function startUserMedia(stream) {
  const ctx = new AudioContext();
  const analyser = ctx.createAnalyser();
  const streamNode = ctx.createMediaStreamSource(stream);
  streamNode.connect(analyser);

  // Create a new volume meter and connect it.
  meter = createAudioMeter(ctx);
  streamNode.connect(meter);

  drawLoop();

}

// Create pitch bar
function drawLoop( time ) {

  var pitchVolume = meter.volume*WIDTH*1.4;

  var width = 0;

  // Pitch detection minimum volume
  var minimum_volume = 130;

  // Get width if Recording started
  if(recordingStarted){

    if(pitchVolume < minimum_volume){
       width = 0;
    }else if(pitchVolume >= minimum_volume && pitchVolume < (minimum_volume+20) ){
       width = 10;
    }else if(pitchVolume >= (minimum_volume+20) && pitchVolume < (minimum_volume+40)){
       width = 20;
    }else if(pitchVolume >= (minimum_volume+40) && pitchVolume < (minimum_volume+60)){
       width = 30;
    }else if(pitchVolume >= (minimum_volume+60) && pitchVolume < (minimum_volume+80)){
       width = 40;
    }else if(pitchVolume >= (minimum_volume+80) && pitchVolume < (minimum_volume+100)){
       width = 50;
    }else if(pitchVolume >= (minimum_volume+100) && pitchVolume < (minimum_volume+120)){
       width = 60;
    }else if(pitchVolume >= (minimum_volume+120) && pitchVolume < (minimum_volume+140)){
       width = 70;
    }else if(pitchVolume >= (minimum_volume+140) && pitchVolume < (minimum_volume+160)){
       width = 80;
    }else if(pitchVolume >= (minimum_volume+160) && pitchVolume < (minimum_volume+180)){
       width = 90;
    }else if(pitchVolume >= (minimum_volume+180)){
       width = 100;
    }

  }

  // Update width
  document.getElementById('voiceVolume').style.width = width+'%';

  rafID = window.requestAnimationFrame( drawLoop );
}

4. Demo

View Demo


5. Conclusion

I displayed the pitch volume bar horizontally which you also display vertically just need to update the CSS.

You can adjust the pitch detection value according to your requirement.

If you found this tutorial helpful then don't forget to share.