Skip to content

Commit

Permalink
playing around with voices
Browse files Browse the repository at this point in the history
  • Loading branch information
DrSpaniel committed Feb 7, 2024
1 parent 75f1be8 commit 5d32c1a
Show file tree
Hide file tree
Showing 30 changed files with 1,330 additions and 0 deletions.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<!-- Library script(s) -->
<script src="js/libraries/p5.min.js"></script>
<script src="js/libraries/p5.sound.min.js"></script>
<script src="js/libraries/p5.speech.js"></script>

<!-- My script(s) -->
<script src="js/script.js"></script>
Expand Down
375 changes: 375 additions & 0 deletions projects/Wint24/TemplateP5Voice/js/libraries/p5.speech.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
/*! p5.speech.js v0.0.1 2015-06-12 */
/* updated v0.0.2 2017-10-17 */
/* updated v0.0.3 2022.1.7 */
/**
* @module p5.speech
* @submodule p5.speech
* @for p5.speech
* @main
*/
/**
* p5.speech
* R. Luke DuBois ([email protected])
* ABILITY Lab / Integrated Design & Media
* New York University
* The MIT License (MIT).
*
* https://github.com/IDMNYU/p5.js-speech
*
* Web Speech API: https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
* Web Speech Recognition API: https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd)
define('p5.speech', ['p5'], function (p5) { (factory(p5));});
else if (typeof exports === 'object')
factory(require('../p5'));
else
factory(root['p5']);
}(this, function (p5) {
// =============================================================================
// p5.Speech
// =============================================================================


/**
* Base class for a Speech Synthesizer
*
* @class p5.Speech
* @constructor
*/
p5.Speech = function(_dv, _callback) {

//
// speech synthesizers consist of a single synthesis engine
// per window instance, and a variable number of 'utterance'
// objects, which can be cached and re-used for, e.g.
// auditory UI.
//
// this implementation assumes a monolithic (one synth,
// one phrase at a time) system.
//

// make a speech synthizer (this will load voices):
this.synth = window.speechSynthesis;

// make an utterance to use with this synthesizer:
this.utterance = new SpeechSynthesisUtterance();

this.isLoaded = 0; // do we have voices yet?

// do we queue new utterances upon firing speak()
// or interrupt what's speaking:
this.interrupt = false;

// callback properties to be filled in within the p5 sketch
// if the author needs custom callbacks:
this.onLoad; // fires when voices are loaded and synth is ready
this.onStart; // fires when an utterance begins...
this.onPause; // ...is paused...
this.onResume; // ...resumes...
this.onEnd; // ...and ends.

this.voices = []; // array of available voices (dependent on browser/OS)

// first parameter of constructor is an initial voice selector
this.initvoice;
if(_dv !== undefined) this.initvoice=_dv;
if(_callback !== undefined) this.onLoad =_callback;

var that = this; // aliasing 'this' into a fixed variable

// onvoiceschanged() fires automatically when the synthesizer
// is configured and has its voices loaded. you don't need
// to wait for this if you're okay with the default voice.
//
// we use this function to load the voice array and bind our
// custom callback functions.
window.speechSynthesis.onvoiceschanged = function() {
if(that.isLoaded==0) { // run only once
that.voices = window.speechSynthesis.getVoices();
that.isLoaded = 1; // we're ready
console.log("p5.Speech: voices loaded!");

if(that.initvoice!=undefined) {
that.setVoice(that.initvoice); // set a custom initial voice
console.log("p5.Speech: initial voice: " + that.initvoice);
}

// fire custom onLoad() callback, if it exists:
if(that.onLoad!=undefined) that.onLoad();

//
// bind other custom callbacks:
//

that.utterance.onstart = function(e) {
//console.log("STARTED");
if(that.onStart!=undefined) that.onStart(e);
};
that.utterance.onpause = function(e) {
//console.log("PAUSED");
if(that.onPause!=undefined) that.onPause(e);
};
that.utterance.onresume = function(e) {
//console.log("RESUMED");
if(that.onResume!=undefined) that.onResume(e);
};
that.utterance.onend = function(e) {
//console.log("ENDED");
if(that.onEnd!=undefined) that.onEnd(e);
};
}
};

}; // end p5.Speech constructor


// listVoices() - dump voice names to javascript console:
p5.Speech.prototype.listVoices = function() {
if(this.isLoaded)
{
for(var i = 0;i<this.voices.length;i++)
{
console.log(this.voices[i].name);
}
}
else
{
console.log("p5.Speech: voices not loaded yet!")
}
};

// setVoice() - assign voice to speech synthesizer, by name
// (using voices found in the voices[] array), or by index.
p5.Speech.prototype.setVoice = function(_v) {
// type check so you can set by label or by index:
if(typeof(_v)=='string') this.utterance.voice = this.voices.filter(function(v) { return v.name == _v; })[0];
else if(typeof(_v)=='number') this.utterance.voice = this.voices[Math.min(Math.max(_v,0),this.voices.length-1)];
};

// volume of voice. API range 0.0-1.0.
p5.Speech.prototype.setVolume = function(_v) {
this.utterance.volume = Math.min(Math.max(_v, 0.0), 1.0);
};

// rate of voice. not all voices support this feature.
// API range 0.1-2.0. voice will crash out of bounds.
p5.Speech.prototype.setRate = function(_v) {
this.utterance.rate = Math.min(Math.max(_v, 0.1), 2.0);
};

// pitch of voice. not all voices support this feature.
// API range >0.0-2.0. voice will crash out of bounds.
p5.Speech.prototype.setPitch = function(_v) {
this.utterance.pitch = Math.min(Math.max(_v, 0.01), 2.0);
};

// sets the language of the voice.
p5.Speech.prototype.setLang = function(_lang) {
this.utterance.lang = _lang;
}

// speak a phrase through the current synthesizer:
p5.Speech.prototype.speak = function(_phrase) {
if(this.interrupt) this.synth.cancel();
this.utterance.text = _phrase;

this.synth.speak(this.utterance);
};

// not working...
p5.Speech.prototype.pause = function() {
this.synth.pause();
};

// not working...
p5.Speech.prototype.resume = function() {
this.synth.resume();
};

// stop current utterance:
p5.Speech.prototype.stop = function() {
// not working...
//this.synth.stop();
this.synth.cancel();
};

// kill synthesizer completely, clearing any queued utterances:
p5.Speech.prototype.cancel = function() {
this.synth.cancel(); // KILL SYNTH
};

// Setting callbacks with functions instead
p5.Speech.prototype.started = function(_cb) {
this.onStart = _cb;
}

p5.Speech.prototype.ended = function(_cb) {
this.onEnd = _cb;
}

p5.Speech.prototype.paused = function(_cb) {
this.onPause = _cb;
}

p5.Speech.prototype.resumed = function(_cb) {
this.onResume = _cb;
}

// =============================================================================
// p5.SpeechRec
// =============================================================================


/**
* Base class for a Speech Recognizer
*
* @class p5.SpeechRec
* @constructor
*/
p5.SpeechRec = function(_lang, _callback) {

//
// speech recognition consists of a recognizer object per
// window instance that returns a JSON object containing
// recognition. this JSON object grows when the synthesizer
// is in 'continuous' mode, with new recognized phrases
// appended into an internal array.
//
// this implementation returns the full JSON, but also a set
// of simple, query-ready properties containing the most
// recently recognized speech.
//

// make a recognizer object.
if('webkitSpeechRecognition' in window) {
this.rec = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)();
}
else {
this.rec = new Object();
console.log("p5.SpeechRec: Speech Recognition not supported in this browser.");
}

// first parameter is language model (defaults to empty=U.S. English)
// no list of valid models in API, but it must use BCP-47.
// here's some hints:
// http://stackoverflow.com/questions/14257598/what-are-language-codes-for-voice-recognition-languages-in-chromes-implementati
if(_lang !== undefined) this.rec.lang=_lang;

// callback properties to be filled in within the p5 sketch
// if the author needs custom callbacks:
this.onResult; // fires when something has been recognized
this.onStart; // fires when the recognition system is started...
this.onError; // ...has a problem (e.g. the mic is shut off)...
this.onEnd; // ...and ends (in non-continuous mode).
if(_callback !== undefined) this.onResult=_callback;

// recognizer properties:

// continous mode means the object keeps recognizing speech,
// appending new tokens to the internal JSON.
this.continuous = false;
// interimResults means the object will report (i.e. fire its
// onresult() callback) more frequently, rather than at pauses
// in microphone input. this gets you quicker, but less accurate,
// results.
this.interimResults = false;

// result data:

// resultJSON:
// this is a full JSON returned by onresult(). it consists of a
// SpeechRecognitionEvent object, which contains a (wait for it)
// SpeechRecognitionResultList. this is an array. in continuous
// mode, it will be appended to, not cleared. each element is a
// SpeechRecognition result, which contains a (groan)
// SpeechRecognitionAlternative, containing a 'transcript' property.
// the 'transcript' is the recognized phrase. have fun.
this.resultJSON;
// resultValue:
// validation flag which indicates whether the recognizer succeeded.
// this is *not* a metric of speech clarity, but rather whether the
// speech recognition system successfully connected to and received
// a response from the server. you can construct an if() around this
// if you're feeling worried.
this.resultValue;
// resultString:
// the 'transcript' of the most recently recognized speech as a simple
// string. this will be blown out and replaced at every firing of the
// onresult() callback.
this.resultString;
// resultConfidence:
// the 'confidence' (0-1) of the most recently recognized speech, e.g.
// that it reflects what was actually spoken. you can use this to filter
// out potentially bogus recognition tokens.
this.resultConfidence;

var that = this; // aliasing 'this' into a fixed variable

// onresult() fires automatically when the recognition engine
// detects speech, or times out trying.
//
// it fills up a JSON array internal to the webkitSpeechRecognition
// object. we reference it over in our struct here, and also copy
// out the most recently detected phrase and confidence value.
this.rec.onresult = function(e) {
that.resultJSON = e; // full JSON of callback event
that.resultValue = e.returnValue; // was successful?
// store latest result in top-level object struct
that.resultString = e.results[e.results.length-1][0].transcript.trim();
that.resultConfidence = e.results[e.results.length-1][0].confidence;
if(that.onResult!=undefined) that.onResult();
};

// fires when the recognition system starts (i.e. when you 'allow'
// the mic to be used in the browser).
this.rec.onstart = function(e) {
if(that.onStart!=undefined) that.onStart(e);
};
// fires on a client-side error (server-side errors are expressed
// by the resultValue in the JSON coming back as 'false').
this.rec.onerror = function(e) {
if(that.onError!=undefined) that.onError(e);
};
// fires when the recognition finishes, in non-continuous mode.
this.rec.onend = function() {
if(that.onEnd!=undefined) that.onEnd();
};

}; // end p5.SpeechRec constructor

// start the speech recognition engine. this will prompt a
// security dialog in the browser asking for permission to
// use the microphone. this permission will persist throughout
// this one 'start' cycle. if you need to recognize speech more
// than once, use continuous mode rather than firing start()
// multiple times in a single script.
p5.SpeechRec.prototype.start = function(_continuous, _interim) {
if('webkitSpeechRecognition' in window) {
if(_continuous !== undefined) this.continuous = _continuous;
if(_interim !== undefined) this.interimResults = _interim;
this.rec.continuous = this.continuous;
this.rec.interimResults = this.interimResults;
this.rec.start();
}
};

// Add function to stop the speech recognition from continued listening
p5.SpeechRec.prototype.stop = function() {
if('webkitSpeechRecognition' in window) {
this.rec.stop();
}
};

}));

/*
todo:
* fix callbacks (pause, resume) in synthesizer.
* support speech grammar models for scoped auditory UI.
* support markdown, boundaries, etc for better synthesis tracking.
* support utterance parser for long phrases.
*/

// EOF
File renamed without changes.
3 changes: 3 additions & 0 deletions projects/Wint24/voicetest1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Template p5 project

This is the README file for the entire project. For more official projects you should write information here about the nature of the project, your name, any special explanations of how the project works, etc.
Binary file added projects/Wint24/voicetest1/assets/images/bg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading

0 comments on commit 5d32c1a

Please sign in to comment.