I'm using a Singleton to get an audio recorder but getUserMedia is asynchronous and I must wait the callbacks end to return the recorder object. It's probably not a good choice to mix synchronous and asynchronous but I don't see how I can do because I can't make getUserMedia synchronous.
The idea is to call SoundRecorder.getInstance() which is going to ask the user to allow the microphone access one for all and to be able to get the recorder instance any time I need.
The problem is at the first call:
var recorder = SoundRecorder.getInstance();
recorder value is "undefined" then it works fine for the other calls.
This is my code below. Thanks for your help.
var SoundRecorder = (function() {
var instance = null;
function init () {
var dfd = $.Deferred();
if (instance == null) {
navigator.getUserMedia({audio: true},
function(stream) {
var audioContext = new AudioContext();
var input = audioContext.createMediaStreamSource(stream);
console.log('Media stream created.');
input.connect(audioContext.destination);
console.log('Input connected to audio context destination.');
instance = new Recorder(input);
dfd.resolve('recorder created');
},
function (e) {
dfd.reject('No live audio input');
notify('error', 'No live audio input: ' + e);
});
} else {
dfd.resolve('recorder already created');
}
return dfd.promise();
}
function waitFor(p){
if (p.state() == "resolved" || p.state() == "rejected") {
return instance;
} else {
setTimeout(waitFor, 500, p);
}
}
return {
getInstance: function() {
var promise = init();
promise.then(function () {
console.log('init success');
}, function () {
console.log('init failed');
});
return waitFor(promise);
}
}
})();
Finally I gave up using the singleton to only use call backs. I made the trick to just call getUserMedia once with an input hidden field that I use to handle different status: "enabled" if the user has allowed the microphone access, "disabled" if web audio not supported, "access" when the access is asked to the user. I think not so elegant but it works. I hope this can help somebody one day ;-)
Related
I'm trying to implement a WebSocket with a fallback to polling. If the WebSocket connection succeeds, readyState becomes 1, but if it fails, readyState is 3, and I should begin polling.
I tried something like this:
var socket = new WebSocket(url);
socket.onmessage = onmsg;
while (socket.readyState == 0)
{
}
if (socket.readyState != 1)
{
// fall back to polling
setInterval(poll, interval);
}
I was expecting socket.readyState to update asynchronously, and allow me to read it immediately. However, when I run this, my browser freezes (I left it open for about half a minute before giving up).
I thought perhaps there was an onreadyStateChanged event, but I didn't see one in the MDN reference.
How should I be implementing this? Apparently an empty loop won't work, and there is no event for this.
This is simple and it work perfectly... you can add condition about maximal time, or number of try to make it more robust...
function sendMessage(msg){
// Wait until the state of the socket is not ready and send the message when it is...
waitForSocketConnection(ws, function(){
console.log("message sent!!!");
ws.send(msg);
});
}
// Make the function wait until the connection is made...
function waitForSocketConnection(socket, callback){
setTimeout(
function () {
if (socket.readyState === 1) {
console.log("Connection is made")
if (callback != null){
callback();
}
} else {
console.log("wait for connection...")
waitForSocketConnection(socket, callback);
}
}, 5); // wait 5 milisecond for the connection...
}
Here is a more elaborate explanation. First off, check the specific browser API, as not all browsers will be on the latest RFC. You can consult the
You don't want to run a loop to constantly check the readystate, it's extra overhead you don't need. A better approach is to understand all of the events relevant to a readystate change, and then wire them up appropriately. They are as follows:
onclose An event listener to be called when the WebSocket connection's readyState changes to CLOSED. The listener receives a CloseEvent named "close".
onerror An event listener to be called when an error occurs. This is a simple event named "error".
onmessage An event listener to be called when a message is received from the server. The listener receives a MessageEvent named "message".
onopen An event listener to be called when the WebSocket connection's readyState changes to OPEN; this indicates that the connection is ready to send and receive data. The event is a simple one with the name "open".
JS is entirely event driven, so you need to just wire up all of these events and check for the readystate, this way you can switch from WS to polling accordingly.
I recommend you look at the Mozilla reference, it's easier to read than the RFC document and it will give you a good overview of the API and how it works (link).
Don't forget to do a callback for a retry if you have a failure and poll until the callback for a successful reconnect is fired.
I am not using pooling at all. Instead, I use queuing.
First I create new send function and a queue:
var msgs = []
function send (msg) {
if (ws.readyState !== 1) {
msgs.push(msg)
} else {
ws.send(msg)
}
}
Then I need to read and send when the connection is first established:
function my_element_click () {
if (ws == null){
ws = new WebSocket(websocket_url)
ws.onopen = function () {
while (msgs.length > 0) {
ws.send(msgs.pop())
}
}
ws.onerror = function(error) {
// do sth on error
}
}
msg = {type: 'mymessage', data: my_element.value}
send(JSON.stringify(msg))
}
WebSocket connection in this example is created only on the first click. Usually, on second messages start to be sent directly.
Look on http://dev.w3.org/html5/websockets/
Search for "Event handler" and find the Table.
onopen -> open
onmessage -> message
onerror ->error
onclose ->close
function update(e){ /*Do Something*/};
var ws = new WebSocket("ws://localhost:9999/");
ws.onmessage = update;
If you use async/await and you just want to wait until the connection is available I would suggest this function :
async connection (socket, timeout = 10000) {
const isOpened = () => (socket.readyState === WebSocket.OPEN)
if (socket.readyState !== WebSocket.CONNECTING) {
return isOpened()
}
else {
const intrasleep = 100
const ttl = timeout / intrasleep // time to loop
let loop = 0
while (socket.readyState === WebSocket.CONNECTING && loop < ttl) {
await new Promise(resolve => setTimeout(resolve, intrasleep))
loop++
}
return isOpened()
}
}
Usage (in async function) :
const websocket = new WebSocket('...')
const opened = await connection(websocket)
if (opened) {
websocket.send('hello')
}
else {
console.log("the socket is closed OR couldn't have the socket in time, program crashed");
return
}
tl;dr
A simple proxy wrapper to add state event to WebSocket which will be emitted when its readyState changes:
const WebSocketProxy = new Proxy(WebSocket, {
construct: function(target, args) {
// create WebSocket instance
const instance = new target(...args);
//internal function to dispatch 'state' event when readyState changed
function _dispatchStateChangedEvent() {
instance.dispatchEvent(new Event('state'));
if (instance.onstate && typeof instance.onstate === 'function') {
instance.onstate();
}
}
//dispatch event immediately after websocket was initiated
//obviously it will be CONNECTING event
setTimeout(function () {
_dispatchStateChangedEvent();
}, 0);
// WebSocket "onopen" handler
const openHandler = () => {
_dispatchStateChangedEvent();
};
// WebSocket "onclose" handler
const closeHandler = () => {
_dispatchStateChangedEvent();
instance.removeEventListener('open', openHandler);
instance.removeEventListener('close', closeHandler);
};
// add event listeners
instance.addEventListener('open', openHandler);
instance.addEventListener('close', closeHandler);
return instance;
}
});
A long explanation:
You can use a Proxy object to monitor inner WebSocket state.
This is a good article which explains how to do it Debugging WebSockets using JS Proxy Object
And here is an example of code snippet from the article above in case the site won't be available in the future:
// proxy the window.WebSocket object
var WebSocketProxy = new Proxy(window.WebSocket, {
construct: function(target, args) {
// create WebSocket instance
const instance = new target(...args);
// WebSocket "onopen" handler
const openHandler = (event) => {
console.log('Open', event);
};
// WebSocket "onmessage" handler
const messageHandler = (event) => {
console.log('Message', event);
};
// WebSocket "onclose" handler
const closeHandler = (event) => {
console.log('Close', event);
// remove event listeners
instance.removeEventListener('open', openHandler);
instance.removeEventListener('message', messageHandler);
instance.removeEventListener('close', closeHandler);
};
// add event listeners
instance.addEventListener('open', openHandler);
instance.addEventListener('message', messageHandler);
instance.addEventListener('close', closeHandler);
// proxy the WebSocket.send() function
const sendProxy = new Proxy(instance.send, {
apply: function(target, thisArg, args) {
console.log('Send', args);
target.apply(thisArg, args);
}
});
// replace the native send function with the proxy
instance.send = sendProxy;
// return the WebSocket instance
return instance;
}
});
// replace the native WebSocket with the proxy
window.WebSocket = WebSocketProxy;
Just like you defined an onmessage handler, you can also define an onerror handler. This one will be called when the connection fails.
var socket = new WebSocket(url);
socket.onmessage = onmsg;
socket.onerror = function(error) {
// connection failed - try polling
}
Your while loop is probably locking up your thread. Try using:
setTimeout(function(){
if(socket.readyState === 0) {
//do nothing
} else if (socket.readyState !=1) {
//fallback
setInterval(poll, interval);
}
}, 50);
In my use case, I wanted to show an error on screen if the connection fails.
let $connectionError = document.getElementById("connection-error");
setTimeout( () => {
if (ws.readyState !== 1) {
$connectionError.classList.add( "show" );
}
}, 100 ); // ms
Note that in Safari (9.1.2) no error event gets fired - otherwise I would have placed this in the error handler.
I am going to make screen sharing function using webRTC.
My code is working well when video calling
But in audio call status, that is not working.
Here is my code.
This is for create peer Connection and add stream for audio calling
const senders = [];
var mediaConstraints = {audio: true, video: false}
navigator.mediaDevices.getUserMedia(mediaConstraints)
.then(function (localStream) {
localLiveStream = localStream;
document.getElementById("local_video").srcObject = localLiveStream;
localLiveStream.getTracks().forEach(track => senders.push(myPeerConnection.addTrack(track, localLiveStream)));
})
.catch(handleGetUserMediaError);
when screen share field
mediaConstraints.video = true;
let displayStream = await navigator.mediaDevices.getDisplayMedia(mediaConstraints)
if (displayStream) {
document.getElementById("local_video").srcObject = displayStream;
console.log("senders: ", senders);
try {
senders.find(sender => sender.track.kind === 'video').replaceTrack(displayStream.getTracks()[0]);
} catch (e) {
console.log("Error: ", e)
}
}
In screen sharing status, sender.track.kind is "audio"
So
senders.find(sender => sender.track.kind === 'video') = null.
As this, replaceTrack makes error
is there any other way for screen share?
You need to add a video track in order to achieve this. It will require renegotiation.
So add the screen track (not replace) to the connection and then create the offer again!
connection.addTrack(screenVideoTrack);
Check this for reference:
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onnegotiationneeded
I am working through some old legacy code dealing with network requests using RPC/YUI library. It essentially creates tags to handle network requests. There are no promises for these.Also, because of IE11 support, we cannot use the native Promise object. Our build process does not utilize any NPM dependencies, so we cannot use any babel related polyfills.
There is a bug I am working on to fix that the argument ignoreError gets overwritten each time another function calls the same function....obviously! We have multiple functions calling this network request function library. Sometimes we want to ignore an error, sometimes we do not.
What is the ideal way to store the multiple requests made and their respective error callbacks so the appropriate item is called?
example:
var rpcUrl,
rpcRetries,
rpcIgnoreError;
// main function that sets some globals:
rpc: function(url, retries, ignoreError) {
rpcUrl = url;
rpcRetries = retries;
rpcIgnoreError = ignoreError;
this.doRpc();
},
// calls the YUI library to initialize network script:
doRpc: function() {
YAHOO.util.Get.script(rpcUrl, {
onFailure: function() {
this.callbackError(true);
},
timeout: 55000
});
},
// YUI callback
callbackError: function(retry) {
if (retry && rpcRetries > 0) {
rpcRetries = rpcRetries - 1;
this.doRpc();
} else {
// ** how do i know this error handling is for the script which failed?
if (!rpcIgnoreError) {
this.populateFormStatus(6);
}
}
},
now, we have multiple functions calling rpc() such as:
sendConfig: function() {
this.rpc(urlForEndpoint, 3, true);
},
sendUser: function() {
this.rpc(urlForEndpoint, 3, false);
},
sendWidget: function() {
this.rpc(urlForEndpoint, 3, false);
},
I am concerned making an array of callbacks will not guarantee that each item is handled with its respective handler.
I could do something like create a map constant:
var RPC_ERR_CB = {
sendConfig: false,
sendUser: true,
sendWidget: true
};
// and then in the onFailure callback, I can read the src of the script tag:
...
doRpc: function() {
YAHOO.util.Get.script(rpcUrl, {
onFailure: function() {
var hasCB = Object.keys(RPC_ERR_CB).some(function(item) {
return arguments[0].src.indexOf(RPC_ERR_CB[item]) <= 0;
});
if (hasCB) {
this.callbackError(true);
}
},
timeout: 55000
});
},
Hope this makes sense...THANKS!
You could pass the values into doRpc, then you can pass it to callbackError or handle it in doRpc (like your example code at the end). This will prevent the global variable from changing on you.
If you're not able to use Promises or ES6 Classes, your options become somewhat limited. If at all possible, I would recommend biting the bullet on getting a Babel transpilation process so you can take advantage of newer features without needing to drop IE11 support.
As it is now though, ideally you don't want to track every request in a global variable somewhere. You can handle each transaction independently by creating each request as a self-contained object:
function RpcRequest (url, retries, ignoreError) {
this.url = url
this.retries = retries
this.ignoreError = ignoreError
}
RpcRequest.prototype.send = function() {
YAHOO.util.Get.script(this.url, {
onFailure: function() {
this.callbackError(true);
},
timeout: 55000
});
}
RpcRequest.prototype.callbackError = function(retry) {
if (retry && this.retries > 0) {
this.retries = this.retries - 1;
this.send();
} else {
if (!this.ignoreError) {
// ...
}
}
}
// Somewhere else, initiate a request
var requestOne = new RpcRequest("http://blah", 3, false)
requestOne.send()
Something I noted when looking over your code: the code that's creating the request has no idea whether the request succeeded or not. And when you have an error, the calling context doesn't know anything about that error. I took a look at the library you mentioned, and it does appear to have some context that you can pass along.
If I were to rewrite this a little bit, I'd do something like this to bubble the error up to your calling context:
RpcRequest.prototype.send = function(callback) {
YAHOO.util.Get.script(this.url, {
onFailure: function(context) {
if( this.ignoreError ) {
context.ignoredError = true
callback(null, context);
return;
}
var retError = new Error('Failure doing something!');
retError.context = context;
callback(retError);
},
onSuccess: function(context) {
callback(null, context);
},
timeout: 55000
});
}
// Somewhere else in the code...
sendWidget: function() {
var request = new RpcRequest(urlForEndpoint, 3, false)
request.send(function(err, result) {
if( err ) {
console.error('Failed at doing a widget thing:', err.context);
// maybe even:
// throw err;
return;
}
if( result.ignoredError ) {
console.warn('Ignored an error on the widget thing:', result);
return;
}
console.log('Success on the widget thing!', result);
})
}
I have an Ionic app in which I use a database. I want to fill this database with the contents of a file.
This part I got working. I want to create a DB.ready() event, much like the $ionicPlatform.ready() or document.ready(), as I need to wait until the database is loaded until I query it.
I am fairly new to Ionic, and to the concept of Promises, so it might be something simple.
I've gotten it to work in Android, but iOS keeps returning an error in the query with "someTablename does not exist". I've placed multiple console.log(), and according to those everything is fine.
Could anyone tell me which part I did incorrect, or another method if that is more common in this situation (again, I'm new, so don't know what is common)?
I expected to get "query" logged every query, but it doesn't do that, is that significant?
// L35_DB - Databaseclass for apps
.factory('L35_DB', ['$ionicPlatform','$cordovaFile','$cordovaSQLite', function($ionicPlatform, $cordovaFile,$cordovaSQLite) {
var L35_DB = {db_start : false};
//-------------------------------------
DB_READY = new Promise(function(resolve,reject){
console.log("query");
if( L35_DB.db_start ){console.log("b"); resolve("Stuff worked!"); }
else{
var filename='fileWithDB.db';
$ionicPlatform.ready(function() {
if( window.cordova ){
return window.plugins.sqlDB.copy(filename, 0,
function(info){ loadDB(filename).then( function(){ console.log("First load", info); resolve("DB loaded?"); }) },
function(info){ loadDB(filename).then( function(){ console.log("Other loads", info); resolve("DB loaded?"); }) }
);
}
});
}
});
//-------------------------------------
// Load the file
function loadDB(filename){
var loading = new Promise(function(resolve,reject){
db = window.sqlitePlugin.openDatabase(
{name: filename, location: 'default'},
function(){
console.log("loadDB success"); // <--- fired
L35_DB.db_start = true; // true, so next call we don't do all this
resolve("DB ready loading");
},
function(err){ reject(err);}
);
});
return loading;
}
//-------------------------------------
// Query -
var _query = function(query,values){
if( !L35_DB.db_start ){
console.error("DB not init");
return false;
}
else if( window.cordova ){
values = values || [];
var actualQuery = new Promise(function(resolve,reject){
db.executeSql(query, values, resolve, reject);
})
return actualQuery;
}
}
//-------------------------------------
return {
query : _query
};
}])
Throughout my app I do:
DB_READY.then(function () {
L35_DB.query("SELECT * FROM systems").then(function (result) {
// Something something something darkside
})
})
After a lot of testing and digging, turns out window.plugins.sqlDB.copy() was the culprit.
The 2nd value, location, can be changed. It defaults to 0, but for iOS it has to be 2. After this change, everything work exactly as expected.
This function should preload the database for Android and iOS, assumed a bit too early it actually did.
I have an Observable coming from an EventEmitter which is really just a http connection, streaming events.
Occasionally I have to disconnect from the underlying stream and reconnect. I am not sure how to handle this with rxjs.
I am not sure if i can complete a source and then dynamically add other "source" to the source, or if I have to do something like i have at the very bottom.
var Rx = require('rx'),
EventEmitter = require('events').EventEmitter;
var eventEmitter = new EventEmitter();
var eventEmitter2 = new EventEmitter();
var source = Rx.Observable.fromEvent(eventEmitter, 'data')
var subscription = source.subscribe(function (data) {
console.log('data: ' + data);
});
setInterval(function() {
eventEmitter.emit('data', 'foo');
}, 500);
// eventEmitter stop emitting data, underlying connection closed
// now attach seconds eventemitter (new connection)
// something like this but obvouisly doesn't work
source
.fromEvent(eventEmitter2, 'data')
Puesdo code that is more of what i am doing, I am creating a second stream connection before I close the first, so i don't "lose" any data. Here i am not sure how to stop the Observable without "losing" records due to onNext not being called due to the buffer.
var streams = [], notifiers = [];
// create initial stream
createNewStream();
setInterval(function() {
if (params of stream have changed) createNewStream();
}, $1minutes / 3);
function createNewStream() {
var stream = new eventEmitterStream();
stream.once('connected', function() {
stopOthers();
streams.push(stream);
createSource(stream, 'name', 'id');
});
}
function stopOthers() {
while(streams.length > 0) {
streams.pop().stop(); // stop the old stream
}
while(notifiers.length > 0) {
// if i call this, the buffer may lose records, before onNext() called
//notifiers.pop()(Rx.Notification.createOnCompleted());
}
}
function createObserver(tag) {
return Rx.Observer.create(
function (x) {
console.log('Next: ', tag, x.length, x[0], x[x.length-1]);
},
function (err) {
console.log('Error: ', tag, err);
},
function () {
console.log('Completed', tag);
});
}
function createSource(stream, event, id) {
var source = Rx.Observable
.fromEvent(stream, event)
.bufferWithTimeOrCount(time, max);
var subscription = source.subscribe(createObserver(id));
var notifier = subscription.toNotifier();
notifiers.push(notifier);
}
First and formost, you need to make sure you can remove all listeners from your previously "dead" emitter. Otherwise you'll create a leaky application.
It seems like the only way you'll know that an EventEmitter has died is to watch frequency, unless you have an event that fires on error or completion (for disconnections). The latter is much, much more preferrable.
Regardless, The secret sauce of Rx is making sure to wrap your data stream creation and teardown in your observable. If wrap the creation of the emitter in your observable, as well as a means to tear it down, you'll be able to use awesome things like the retry operator to recreate that observable.
So if you have no way of knowing if it died, and you want to reconnect it, you can use something like this:
// I'll presume you have some function to get an EventEmitter that
// is already set up
function getEmitter() {
var emitter = new EventEmitter();
setInterval(function(){
emitter.emit('data', 'foo');
}, 500)
return emitter;
}
var emitterObservable = Observable.create(function(observer) {
// setup the data stream
var emitter = getEmitter();
var handler = function(d) {
observer.onNext(d);
};
emitter.on('data', handler);
return function() {
// tear down the data stream in your disposal function
emitter.removeListener('on', handler);
};
});
// Now you can do Rx magic!
emitterObservable
// if it doesn't emit in 700ms, throw a timeout error
.timeout(700)
// catch all* errors and retry
// this means the emitter will be torn down and recreated
// if it times out!
.retry()
// do something with the values
.subscribe(function(x) { console.log(x); });
* NOTE: retry catches all errors, so you may want to add a catch above it to handle non-timeout errors. Up to you.