Web Speech API - Local voices not functioning - Mac OSX - javascript

I've been trying to use speech synthesis provided by the Web Speech API. None of the examples I found online were working except one which picked voices at random. I realized the only voices working were those provided by google rather than the local voices provided by OSX. My guess is there is some OSX permission I need to enable so the browser can use those voices. Here is my code:
let utterance = new SpeechSynthesisUtterance();
let voices = [];
let isLoaded = false;
window.speechSynthesis.onvoiceschanged = () => {
if (!isLoaded) {
voices = window.speechSynthesis.getVoices();
isLoaded = true;
console.log('Voices loaded!', voices);
utterance.voice = voices[50]; // Set your voice by index here
}
};
const setup = () => {
const button = document.getElementById('button');
button.addEventListener('click', e => {
if (isLoaded) {
synth.cancel();
utterance.text = 'Hello World';
utterance.volume = 1;
utterance.rate = 1;
utterance.pitch = 1;
utterance.lang = 'en-US';
console.log(utterance);
synth.speak(utterance);
}
});
};

Related

Javascript Speech Synthesis

Hello people I'm currently trying to develop a web app that utilizes Javascript speech synthesis. The problem I'm facing is that when I run the code in my webpage it reads out the text in a different language than the one I have specified but when I run the same code directly in the console it reads the correct language. Where could the problem be in this code? Note that I'm running the code in Chrome.
var myText = document.getElementById ('number');
var myText2 = document.getElementById('counter');
var msg;
var voices;
var timer = setInterval(function() {
voices = speechSynthesis.getVoices();
console.log(voices);
if (voices.length !== 0) {
msg = new SpeechSynthesisUtterance();
msg.voice = voices[1];
msg.pitch =1;
msg.rate= 0.9;
msg.text = myText.value + myText2.value;
speechSynthesis.speak(msg);
msg.lang = 'en-GB';
clearInterval(timer);
}
}, 200);
timer();

Start running SpeechSynthesis API on my Android and Safari devices

I'm trying to make a web app with the SpeechSynthesis API to run my program after one click on start button and then start listening to the user on my Android and iOS devices. The user could speak anything to run the program. After that, I can play my audio files every three seconds. Below is my code so far. Is my logic wrong? I can't start my program after the click and hear any sound.
Another question is this SpeechSynthesis API could support Android and iOS devices, but when I saw some event such as 'soundstart event', it doesn't support Safari Mobile. What are their relationships? I got really confused. And the SpeechRecognition API only supports for Chrome browser but don't I need to user some event like soundstart?
Thank you for help in advance. I really appreciate it.
<p id="msg" align="center"></p>
<script>
var utterance = new SpeechSynthesisUtterance("Hello");
//window.speechSynthesis.speak(utterance);
var supportMsg = document.getElementById('msg');
if ('speechSynthesis' in window)
{
supportMsg.innerHTML = 'Your browser <strong>supports</strong> speech synthesis.';
console.log("Hi");
utterance.onstart = function(event)
{
console.log('Hhhh')
};
var playList = ["1_hello", "2_how_old", "3_what_did_you_make"];
var dir = "sound/";
var extention = ".wav";
audio.src = dir + playList[audioIndex] + extention;
audio.load();
var audioIndex = 0;
setTimeout(function(){audio.play();}, 1000);
window.speechSynthesis.speak(utterance);
}
else
{
supportMsg.innerHTML = 'Sorry your browser <strong>does not support</strong> speech synthesis.<br>Try this in Chrome Canary.';
}
//window.speechSynthesis(utterance);
</script>
<div class="container">
<button id="runProgram" onclick='utterance.onstart();'
class="runProgram-button">Start</button>
</div>
Does this work for you?
function playAudio() {
var msg = new SpeechSynthesisUtterance('Help me with this code please?');
msg.pitch = 0;
msg.rate = .6;
window.speechSynthesis.speak(msg);
var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();
msg.voice = voices[10]; // Note: some voices don't support altering params
msg.voiceURI = 'native';
msg.volume = 1; // 0 to 1
msg.rate = 1.2; // 0.1 to 10
msg.pitch = 2; //0 to 2
msg.text = 'Sure. This code plays "Hello World" for you';
msg.lang = 'en-US';
msg.onend = function(e) {
var msg1 = new SpeechSynthesisUtterance('Now code plays audios for you');
msg1.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == 'Whisper'; })[0];
msg1.pitch = 2; //0 to 2
msg1.rate= 1.2; //0 to 2
// start your audio loop.
speechSynthesis.speak(msg1);
console.log('Finished in ' + e.elapsedTime + ' seconds.');
};
speechSynthesis.speak(msg);
}
<div class="container">
<button id="runProgram" onclick='playAudio();' class="runProgram-button">Start</button>
</div>

Is it possible to post process HTML5 video elements audio output with Web Audio Api?

I have an html5 video element and I need to apply different processing realtime on the video's output audio. On desktop I made it work with the WebAudio API. The Api is seemingly present on iOS also. I am able to inspect the created objects, but it doesn't modify the video's output signal.
Here's my example code:
$(function () {
window.AudioContext = window.AudioContext||window.webkitAudioContext;
var audioContext = new AudioContext();
var bufferSize = 1024;
var selectedChannel = 0;
var effect = (function() {
var node = audioContext.createScriptProcessor(bufferSize, 2, 2);
node.addEventListener('audioprocess', function(e) {
var input = e.inputBuffer.getChannelData(selectedChannel);
var outputL = e.outputBuffer.getChannelData(0);
var outputR = e.outputBuffer.getChannelData(1);
for (var i = 0; i < bufferSize; i++) {
outputL[i] = selectedChannel==0? input[i] : 0.0;
outputR[i] = selectedChannel==1? input[i] : 0.0;
}
});
return node;
})();
var streamAttached = false;
function attachStream(video) {
if (streamAttached) {
return;
}
var source = audioContext.createMediaElementSource(video);
source.connect(effect);
effect.connect(audioContext.destination);
streamAttached = true;
}
function iOS_video_touch_start() {
var video = $('#vid')[0];
video.play();
attachStream(video);
}
var needtouch = false;
$('#vid').on('play', function () {
attachStream(this);
}).on('loadedmetadata', function () {
this.play();
this.volume=1.0;
if (this && this.paused) {
if (needtouch == false) {
needtouch = true;
this.addEventListener("touchstart", iOS_video_touch_start, true);
}
}
});
window.panToRight = function(){
selectedChannel = 1;
};
window.panToLeft = function(){
selectedChannel = 0;
};
});
You can also check it on CP:
http://codepen.io/anon/pen/pgeJQG
With the buttons you are able to toggle between the left and the right channels. On desktop browsers (Chrome, Firefox, Safari tested) it works fine.
I have also tried the older createJavaScriptNode() instead of createScriptProcessor(). I have also tried it with an alternative effect chain, which was looking like this:
var audioContext = new (window.AudioContext||window.webkitAudioContext)();
audioContext.createGain = audioContext.createGain||audioContext.createGainNode;
var gainL = audioContext.createGain();
var gainR = audioContext.createGain();
gainL.gain.value = 1;
gainR.gain.value = 1;
var merger = audioContext.createChannelMerger(2);
var splitter = audioContext.createChannelSplitter(2);
//Connect to source
source = audioContext.createMediaElementSource(video);
//Connect the source to the splitter
source.connect(splitter, 0, 0);
//Connect splitter' outputs to each Gain Nodes
splitter.connect(gainL, 0);
splitter.connect(gainR, 1);
//Connect Left and Right Nodes to the Merger Node inputs
//Assuming stereo as initial status
gainL.connect(merger, 0, 0);
gainL.connect(merger, 0, 1);
//Connect Merger output to context destination
merger.connect(audioContext.destination, 0, 0);
As you probably noticed this code was using the built in nodes only. But no luck.
So my questions are: Is this even possible on mobile? If it is, than what am I missing? If it is not, than any possible workaround? Thanks
With Chrome on Android, MediaElementSource is not currently routed to WebAudio. This is a known issue and is planned to be fixed eventually.

How to switch audio source with Microphone.getMicrophone() Actionscript 3

I have a webcam streaming app based on the webcam.fla example by Wowza. The app streams audio and video from Flash to a Wowza server where it's transcoded etc.
We're trying to add a feature that lets the audio source be changed to any other system audio source. So far we successfully create a dropdown containing all the interfaces and handle the callback but, despite starting and stopping the stream with the doConnect() function, the audio source seems to remain the default.
import flash.media.*;
import flash.geom.*;
import flash.net.*;
import flash.media.*;// Should this be duplicated
var parsed:Object = root.loaderInfo.parameters;
var nc:NetConnection = null;
var nsPublish:NetStream = null;
var nsPlay:NetStream = null;
var camera:Camera = null;
var microphone:Microphone = null;
// Testing
var serverName:String = "rtmp://stream-na.example.tv:1935/live";
var movieName:String = "streamName";
var flushVideoBufferTimer:Number = 0;
// Quality settings
var videoBitrate:Number = 200000;
var videoQuality:Number = 80; // Quality %
var videoWidth:Number = 640;
var videoHeight:Number = 360;
var videoFrameRate:Number = 30;
//////////////// UI Functions Bellow
import fl.controls.ComboBox;
import fl.data.DataProvider;
var aCb:ComboBox = new ComboBox();
function createAudioComboBox(sources)
{
var sourcesArray:Array = new Array();
aCb.dropdownWidth = 210;
aCb.width = 200;
aCb.move(0, 365);
aCb.prompt = "Change Audio Source";
aCb.dataProvider = new DataProvider(sourcesArray);
aCb.addEventListener(Event.CHANGE, changeAudioHandler);
addChild(aCb);
for (var index in sources)
{
//ExternalInterface.call("logBrowserStreaming", sources[index]);
aCb.addItem( { label: sources[index], data: index} );
}
function changeAudioHandler(event:Event):void
{
doConnect();
//var request:URLRequest = new URLRequest();
//request.url = ComboBox(event.target).selectedItem.data;
//navigateToURL(request);
//aCb.selectedIndex = -1;
var audioSource = ComboBox(event.target).selectedItem.data;
//microphone:Microphone = null;
microphone = Microphone.getMicrophone(audioSource);
microphone.rate = 16;
microphone.codec = SoundCodec.SPEEX;
microphone.encodeQuality = 10; // This is shit!! offer better audio in native app?
microphone.setSilenceLevel(0, -1);
microphone.setUseEchoSuppression(true);
//ExternalInterface.call("logBrowserStreaming", audioSource);
// Trigger restart camera...
//startCamera(); // Nope
doConnect();
}
}
//////////////// Core Streaming Functions Bellow
function startCamera()
{
// get the default Flash camera and microphone
camera = Camera.getCamera();
microphone = Microphone.getMicrophone();
// here are all the quality and performance settings
// here are all the quality and performance settings
if (camera != null)
{
//camera.setMode(1280, 720, 30, false);
camera.setMode(videoWidth, videoHeight, videoFrameRate, false); // false gives framerate priority apparently?? http://www.flash-communications.net/technotes/setMode/index.html
camera.setQuality(videoBitrate, videoQuality);
// Max 800kbps;
camera.setKeyFrameInterval(2);
// List audio sources names
// sourceVideoLabel.text += Camera.names;
// Create audio sources dropdown
// Hide video sources for now...
//createVideoComboBox(Camera.names);
}
else
{
sourceVideoLabel.text = "No Camera Found\n";
}
if ( microphone != null)
{
microphone.rate = 16;
microphone.codec = SoundCodec.SPEEX;
microphone.encodeQuality = 10; // This is shit!! offer better audio in native app?
microphone.setSilenceLevel(0, -1);
microphone.setUseEchoSuppression(true);
// List audio sources names;
// sourceVideoLabel.text += Microphone.names;
// Create audio sources dropdown
createAudioComboBox(Microphone.names);
// Don't show audio slider for now...
// createAudioSlider();
// Don't monitor audio level for now...
//monitorAudioLevel();
}
else
{
sourceVideoLabel.text += "No Microphone Found\n";
}
nameStr.text = movieName;
AppendCheckbox.selected = false;
connect.connectStr.text = serverName;
connect.connectButton.addEventListener(MouseEvent.CLICK, doConnect);
//enablePlayControls(false);
doConnect();
}
function ncOnStatus(infoObject:NetStatusEvent)
{
trace("nc: "+infoObject.info.code+" ("+infoObject.info.description+")");
if (infoObject.info.code == "NetConnection.Connect.Failed")
{
prompt.text = "Connection failed. Try again or email support#chew.tv";
}
else if (infoObject.info.code == "NetConnection.Connect.Rejected")
{
// Hide connect fail...
prompt.text = infoObject.info.description;
}
}
// Ask for permission to use the camera and show the preview to the user
// event:MouseEvent
// doConnect toggles connections on and off.
function doConnect()
{
// connect to the Wowza Media Server
if (nc == null)
{
// create a connection to the wowza media server
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, ncOnStatus);
nc.connect(connect.connectStr.text);
//connect.connectButton.label = "Disconnect";
// uncomment this to monitor frame rate and buffer length
//setInterval("updateStreamValues", 500);
// Attach camera to preview
videoCamera.clear();
videoCamera.attachCamera(camera);
//enablePlayControls(true);
// Pass status to
// ExternalInterface.call("logBrowserStreaming", "cameraagreed");
}
else
{
nsPublish = null;
nsPlay = null;
videoCamera.attachNetStream(null);
videoCamera.clear();
videoRemote.attachNetStream(null);
videoRemote.clear();
nc.close();
nc = null;
//enablePlayControls(false);
doSubscribe.label = 'Play';
doPublish.label = 'Stream';
AppendCheckbox.selected = false;
connect.connectButton.label = "Connect";
prompt.text = "";
}
}
// function to monitor the frame rate and buffer length
function updateStreamValues()
{
if (nsPlay != null)
{
fpsText.text = (Math.round(nsPlay.currentFPS*1000)/1000)+" fps";
bufferLenText.text = (Math.round(nsPlay.bufferLength*1000)/1000)+" secs";
}
else
{
fpsText.text = "";
bufferLenText.text = "";
}
}
function nsPlayOnStatus(infoObject:NetStatusEvent)
{
trace("nsPlay: onStatus: "+infoObject.info.code+" ("+infoObject.info.description+")");
if (infoObject.info.code == "NetStream.Play.StreamNotFound" || infoObject.info.code == "NetStream.Play.Failed")
{
prompt.text = infoObject.info.description;
}
}
function doCloseRecord()
{
// after we have hit "Stop" recording and after the buffered video data has been
// sent to the Wowza Media Server close the publishing stream
nsPublish.publish("null");
}
// this function gets called every 250 ms to monitor the;
// progress of flushing the video buffer. Once the video
// buffer is empty we close publishing stream
function flushVideoBuffer()
{
var buffLen:Number = nsPublish.bufferLength;
if (buffLen == 0)
{
clearInterval(flushVideoBufferTimer);
flushVideoBufferTimer = 0;
doCloseRecord();
doPublish.label = 'Stream';
}
}
function nsPublicOnStatus(infoObject:NetStatusEvent)
{
trace("nsPublish: "+infoObject.info.code+" ("+infoObject.info.description+")");
// After calling nsPublish.publish(false); we wait for a status;
// event of "NetStream.Unpublish.Success" which tells us all the video
// and audio data has been written to the flv file. It is at this time
// that we can start playing the video we just recorded.
if (infoObject.info.code == "NetStream.Unpublish.Success")
{
//doPlayStart();
}
if (infoObject.info.code == "NetStream.Play.StreamNotFound" || infoObject.info.code == "NetStream.Play.Failed")
{
prompt.text = infoObject.info.description;
}
}
function initH264Recording(nsPublish:NetStream)
{
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_3);
nsPublish.videoStreamSettings = h264Settings;
}
// Start recording video to the server
function doStreamStart()
{
//prompt.text = "Starting stream with mic...";
//prompt.text = microphone;
ExternalInterface.call("logBrowserStreaming", "starting stream");
// stop video playback
//doPlayStop();
// create a new NetStream object for publishing
nsPublish = new NetStream(nc);
var nsPublishClient:Object = new Object();
nsPublish.client = nsPublishClient;
// Set the H.264 encoding parameters
if (testVersion(11,0,0,0))
{
initH264Recording(nsPublish);
}
else
{
prompt.text = "Flash player 11 or greater is required for H.264 encoding (" + Capabilities.version + ").";
}// trace the NetStream status information
nsPublish.addEventListener(NetStatusEvent.NET_STATUS, nsPublicOnStatus);
// publish the stream by name;
nsPublish.publish(nameStr.text, (AppendCheckbox.selected?"append":"record"));
// add custom metadata to the header of the .flv file;
var metaData:Object = new Object();
metaData["description"] = "Recorded using WebcamRecording example.";
nsPublish.send("#setDataFrame", "onMetaData", metaData);
// attach the camera and microphone to the server;
nsPublish.attachCamera(camera);
nsPublish.attachAudio(microphone);
ExternalInterface.call("logBrowserStreaming", microphone);
// set the buffer time to 20 seconds to buffer 20 seconds of video;
// data for better performance and higher quality video
nsPublish.bufferTime = 20;
// Disable the audio choice dropdown
aCb.enabled = false;
}
function doStreamStop()
{
ExternalInterface.call("logBrowserStreaming", "stopping stream");
// stop streaming video and audio to the publishing
// NetStream object
nsPublish.attachAudio(null);
nsPublish.attachCamera(null);
// After stopping the publishing we need to check if there is;
// video content in the NetStream buffer. If there is data
// we are going to monitor the video upload progress by calling
// flushVideoBuffer every 250ms. If the buffer length is 0
// we close the recording immediately.
var buffLen:Number = nsPublish.bufferLength;
if (buffLen > 0)
{
flushVideoBufferTimer = setInterval(flushVideoBuffer,250);
doPublish.label = 'Wait...';
}
else
{
trace("nsPublish.publish(null)");
doCloseRecord();
doPublish.label = 'Start';
}
// Disable the audio choice dropdown
aCb.enabled = true;
}
// Test version function checks if the current flash version supports H.264 Encoding.
function testVersion(v0:Number, v1:Number, v2:Number, v3:Number):Boolean
{
var version:String = Capabilities.version;
var index:Number = version.indexOf(" ");
version = version.substr(index+1);
var verParts:Array = version.split(",");
var i:Number;
var ret:Boolean = true;
while (true)
{
if (Number(verParts[0]) < v0)
{
ret = false;
break;
}
else if (Number(verParts[0]) > v0)
{
break;
}
if (Number(verParts[1]) < v1)
{
ret = false;
break;
}
else if (Number(verParts[1]) > v1)
{
break;
}
if (Number(verParts[2]) < v2)
{
ret = false;
break;
}
else if (Number(verParts[2]) > v2)
{
break;
}
if (Number(verParts[3]) < v3)
{
ret = false;
break;
}
break;
}
trace("testVersion: "+Capabilities.version+">="+v0+","+v1+","+v2+","+v3+": "+ret);
return ret;
}
// External trigger from Javascript;
// Allow stream to start with startBrowserStreaming call from js
ExternalInterface.addCallback("startBrowserStreaming", doStreamStart);
// Allow stream to stop with stopBrowserStreaming call from js;
ExternalInterface.addCallback("stopBrowserStreaming", doStreamStop);
stage.align = "TL";
stage.scaleMode = "noScale";
startCamera();
You can switch your audio source without touching the NetConnection and/or the NetStream.
Take this simple example, where I used a button to change my audio source :
const server:String = 'rtmp://localhost/live';
const stream:String = 'live';
var nc:NetConnection;
var ns_publish:NetStream;
nc = new NetConnection();
nc.addEventListener(
NetStatusEvent.NET_STATUS,
function(e:NetStatusEvent):void {
if(e.info.code == 'NetConnection.Connect.Success'){
publish();
}
}
)
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(e:AsyncErrorEvent):void {})
nc.connect(server);
function publish():void {
var cam:Camera = Camera.getCamera();
// for my case, I have 2 mic, and I start with the first
var mic:Microphone = Microphone.getMicrophone(0);
ns_publish = new NetStream(nc);
ns_publish.attachAudio(mic);
ns_publish.attachCamera(cam);
ns_publish.publish(stream, 'record');
}
btn_switch_mic.addEventListener(MouseEvent.CLICK, function(e){
// I can switch to the second mic without initialize my NetConnection and/or my NetStream
var mic:Microphone = Microphone.getMicrophone(1);
ns_publish.attachAudio(mic);
})
I tested this code with Wowza Streaming Engine 4.1.1 (free version without Wowza Transcoder AddOn of course) and Flash Media Server 4.5, and It's working fine.
Note : We can use the same manner to change video source (Camera).
Hope all that can help you.

Detect lock screen or running screensaver with Firefox/OS X

I'm creating an extension for Firefox (SDK Add-on) in which I'll need to detect screensaver and lock-screen events so that I can set a user's availability status in a web-app.
I've managed to do this already for Windows and now need to port to OS X. For the Windows version, I was using calls to native API to find out if screen was locked, etc. Is there a similar way of getting OS information from a Firefox extension on OS X? I've tried Googling this and haven't found a solid answer - any help appreciated!
On OSX you can query a locked screen/screensaver using CGSessionCopyCurrentDictionary and looking for the presence and value of the "CGSSessionScreenIsLocked" key.
This is platform API, so one will have to use js-ctypes again and write a bunch of code to get that working.
I did get it working: The following code is a working example you can run in a privileged Scratchpad. To get a privileged one, open a pad for e.g. about:newtab.
Components.utils.import("resource://gre/modules/ctypes.jsm");
var CoreFoundation = new (function() {
this.CFNumberRef = ctypes.voidptr_t;
this.CFStringRef = ctypes.voidptr_t;
this.CFDictionaryRef = ctypes.voidptr_t;
var lib = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
this.CFRelease = lib.declare(
"CFRelease",
ctypes.default_abi,
ctypes.void_t,
ctypes.voidptr_t);
var CFStringCreateWithCharacters = lib.declare(
"CFStringCreateWithCharacters",
ctypes.default_abi,
this.CFStringRef,
ctypes.voidptr_t,
ctypes.jschar.ptr,
ctypes.int32_t);
this.CFStringCreateWithCharacters = function(str) {
var rv = CFStringCreateWithCharacters(null, str, str.length);
if (!rv || rv.isNull()) {
return null;
}
return ctypes.CDataFinalizer(rv, this.CFRelease);
};
var CFDictionaryGetValue = lib.declare(
"CFDictionaryGetValue",
ctypes.default_abi,
this.CFNumberRef,
this.CFDictionaryRef,
this.CFStringRef);
this.CFDictionaryGetInt = function(dict, str) {
var rv = CFDictionaryGetValue(dict, this.CFStringCreateWithCharacters(str));
if (!rv || rv.isNull()) {
return null;
};
return this.CFNumberGetValue(rv);
};
var CFNumberGetValue = lib.declare(
"CFNumberGetValue",
ctypes.default_abi,
ctypes.bool,
this.CFNumberRef,
ctypes.int32_t,
ctypes.int32_t.ptr);
this.CFNumberGetValue = function(num) {
var rv = new ctypes.int32_t();
CFNumberGetValue(num, 3, rv.address());
console.log("CFNumberGetValue", rv, rv.value);
return rv.value;
};
this.close = function() {
lib.close();
};
})();
var ApplicationServices = new (function() {
var lib = ctypes.open("/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices");
var CGSessionCopyCurrentDictionary = lib.declare(
"CGSessionCopyCurrentDictionary",
ctypes.default_abi,
CoreFoundation.CFDictionaryRef);
this.CGSessionCopyCurrentDictionary = function() {
var rv = CGSessionCopyCurrentDictionary();
if (!rv || rv.isNull()) {
return null;
}
return ctypes.CDataFinalizer(rv, CoreFoundation.CFRelease);
};
this.close = function() {
lib.close();
};
})();
setInterval(function() {
var dict = ApplicationServices.CGSessionCopyCurrentDictionary();
if (dict) {
var locked = CoreFoundation.CFDictionaryGetInt(dict, "CGSSessionScreenIsLocked");
console.log("rv", locked);
if (locked) {
// do something;
}
}
}, 500);

Categories