How can audio started in other functions be stopped? - javascript

I'm trying to create a DTMF keypad emulator using only HTML, CSS, and JavaScript,
I'm having a small problem with sound. When an image is clicked, that runs the offHook() function which starts playing dial tone. However, I want that dial tone to stop as soon as the first number is clicked (or pressed once I had a keyboard listener). For each number function i.e. dial1(), dial2(), etc. which is tied up to the individual buttons that are clicked, I run the numberDial() function, which is supposed to stop the dial tone audio. However, it refuses to stop.
I have tried:
-dialTone.pause();
-dialTone.stop();
-dialTone.src = "";
Regardless, the dial tone continues. How can I get it to stop when numberDial() is run? This function is run every time a number is pressed (I'll add other stuff later) so it will only be stopping the audio the first number, but it shouldn't do any harm the other times either.
My understanding is that the function numberDial() should recognize the variable dialTone - so why is dialTone refusing to pause?
function offHook() {
document.getElementById("WE2500").style.display = "none";
document.getElementById("dialPad").style.display = "block";
var dialTone = new Audio('dialTone.m4a');
dialTone.play();
}
var number = "";
function numberDial() {
dialTone.src = "";
}
function dial1() {
numberDial();
number = number + "1";
var tone1 = new Audio('DTMF-1.wav');
tone1.play();
}

The problem here is "my understanding is that the function numberDial() should recognize the variable dialTone". Your understanding is very wrong indeed: variables declared with an allocator keyword (var,let, or `const) inside functions do not persist outside those functions. This is pretty much how every conventional programming language works, so the fact that you thought it would persist is... curious? It might be worth reading up on how Javascript works.
The only time this is not true is if you don't use an allocator at all to declare your variables, but this is an extreme bad practice: always use an allocator keyword and make sure you're properly scoping them.
So, let's do that: the var dialTone that you define in your offHook() function is only accessible inside the offHook function right. If you need access to it at the global level, it'll have to be declared it at the global level, even if you don't then initialize it until you call your offHook function:
var dialTone;
function offHook() {
...
dialTone = new Audio('dialTone.m4a'); // note: NO 'var'. It already exists.
dialTone.play();
}
function numberDial() {
// we now need to make sure we only stop dialTone if
// the dialTone variable actually points to something.
if (dialTone) {
// see http://stackoverflow.com/questions/14834520 on why this "stops" audio.
dialTone.pause();
dialTone.currentTime = 0;
}
}

Related

Javascript function not fired on video timeupdate

Currently working on a page containing a video that has to be paused at certain points (like chapters). So I made a function that will stop the video when it hits the next "time marker" which looks like this:
function vidPause(nextMarker){
var timeMarker = nextMarker;
if(videoPlayer.currentTime >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', vidPause());
}
};
And I'm trying to fire it this way:
videoPlayer.addEventListener('timeupdate', vidPause(nextMarker));
But it only seems to fire when the video is loaded. Nothing happens when the video is playing (tested by using a console.log(videoPlayer.currentTime); inside the vidPause function).
Note: I need the function to be called that way so that I can remove the event listener when it hits the time marker, that way it won't stop when the user wants to play the video from that point on.
Thanks in advance for any suggestions!
The function is being called once in the addEventListener line, but that's not actually passing it as a callback.
Try this:
function videoUpdate(e) {
vidPause(nextMarker, videoPlayer.currentTime;); // Now call your function
}
function vidPause(nextMarker, timeStamp){
var timeMarker = nextMarker;
if (timeStamp >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', videoUpdate);
}
};
videoPlayer.addEventListener('timeupdate', videoUpdate); // Note no brackets, as it's passing a ref to the function rather than calling it
I don't know what the scope of nextMarker is, but you should be able to start console logging and find out.

node.js setInterval not working in custom module

I am developing a web application in node.js to collect data from devices on a network using snmp. This is my first real encounter with node.js and javascript. In the app each device will be manipulated through a module I named SnmpMonitor.js. This module will maintain basic device data as well as the snmp and database connection.
One of the features of the app is the ability to constantly monitor data from smart metering devices. To do this I created the following code to start and stop the monitoring of the device. It uses setInterval to constantly send a snmp get request to the device. Then the event listener picks it up and will add the collected data to a database. Right now the listener just prints to show it was successful.
var dataOIDs = ["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"];
var intervalDuration = 500;
var monitorIntervalID;
var dataCollectionEvent = "dataCollectionComplete";
var emitter = events.EventEmitter(); // Uses native Event Module
//...
function startMonitor(){
if(monitorIntervalID !== undefined){
console.log("Device monitor has already started");
} else {
monitorIntervalID = setInterval(getSnmp,intervalDuration,dataOIDs,dataCollectionEvent);
emitter.on(dataCollectionEvent,dataCallback);
}
}
function dataCallback(recievedData){
// receivedData is returned from getSnmp completion event
// TODO put data in database
console.log("Event happened");
}
function stopMonitor(){
if(monitorIntervalID !== undefined){
clearInterval(monitorIntervalID);
emitter.removeListener(dataCollectionEvent,dataCallback);
} else {
console.log("Must start collecting data before it can be stopped");
}
}
//...
I also have a test file, test.js, that requires the module, starts monitoring, waits 10 seconds, then stops it.
var test = require("./SnmpMonitor");
test.startMonitor();
setTimeout(test.stopMonitor,10000);
My problem is that the setInterval function in startMonitor() is not being run. I have tried placing console.log("test"); before, inside, and after it to test it. The inside test output never executes. The monitorIntervalID variable is also returned as undefined. I have tested setInterval(function(){ console.log("test"); },500); in my test.js file and it runs fine with no issues. I feel like this is a noobie mistake but I just can't seem to figure out why it won't execute.
Here is a link to the entire module: SnmpMonitor.js
I not sure exactly what was wrong but I got it to work by overhauling the whole class/module. I thought the way I had it was going to allow me to create new monitors objects but I was wrong. Instead I created two functions inside the monitor file that do the same thing. I changed the start function to the following.
SnmpMonitor.prototype.start = function() {
var snmpSession = new SNMP(this.deviceInfo.ipaddress,this.emitter);
var oids = this.deviceInfo.oids;
var emit = this.emitter;
var duration = this.intervalDuration;
this.intervalID = setInterval(function(){
snmpSession.get(dataCollectionEvent,emit,oids);
},duration);
};
The setInterval function seems to work best when the callback function is set inside an anonymous function, even though technically you can pass it directly. Using the this. notation I created some class/module/function variables (whatever its called in js) that are in scope of the whole class. For some reason the variables accessed through this. do not work so well when directly in a function or expression so I created temp variables for them. In my other version all the variables were global and js doesn't seem to like that.

JavaScript callback function with relative variables

I am not entirely sure how to phrase this question, but basically, I have a class, button that on its click should call the function passed to it.
button = function(...,callBack) {
//...
this._cb = callBack;
}
button.prototype.callBack = function(e) {
//...
this._cb();
}
and then somewhere else
//on canvas click
e.target.callBack(e);
(I hope this is about the right amount of background, I can give more if needed)
So the issue I am running into is when I dynamically instantiate the buttons such that their callbacks use data from an array. i.e.
for (var i = 0; i < levels.length; i++) {
buttons[buttons.length] = new button(..., function() {drawLevel(levels[i])});
}
Then when they are clicked, they run that callback code and try to find some random value for i (probably a for-loop that didn't use var) and runs that level.
My question is, how can I (without using eval) circumvent this problem.
Thanks!
I'm not 100% clear on what you're asking, but it looks like you're going to be getting the wrong value for i in the anonymous function you're creating in the loop (it will always be levels.length)
Way around this is to have a different scope for every function created, with the i in each scope being a copy of the i in the loop
buttons[buttons.length] = new button(..., (function(i){
return function() {drawLevel(levels[i])};
})(i));

Avoiding lag caused by hoisting

I want to log 'tapped' and execute the HUD asap, but oauth_upload_photo is causing it to lag (apparently because of hoisting). How can I snap the HUD instantly??
var submit_post = function submit_post(){
console.log('tapped');
// Show HUD
plugins.navigationBar.hideRightButton();
var hud = document.getElementById("hud");
hud.style.display = 'block';
// Get the image
var image = document.getElementById('myImage');
var imageURI = image.src;
// Get the caption from the textarea
var cap = document.getElementById('tar');
var caption = cap.value;
// Call upload photo
oauth_upload_photo(imageURI,caption);
};
Your issue (which needs a lot more explanation before we could understand what you're actually asking about) has nothing to do with javascript variable hoisting. All hoisting does is cause variables to be defined at the top of the function,regardless of where their initial declaration is located in the function. It doesn't change the execution order of any statements.
Also, in some browsers console.log() is not guaranteed to be completely synchronous and the display of the data in the log window is not necessarily immediate either. There is sometimes a delay before it actually logs. I don't know if this is caused by marshalling data across process boundaries, general repaint logic or some other internal implementation issue.
You may also want to change this:
var submit_post = function submit_post(){
to this:
var submit_post = function (){
or even this:
function submit_post() {
so you aren't double defining the same symbol.

setTimeout in methods of multiple objects of the same type

I need help with the use of "setTimeout" in the methods of the objects of the same type. I use this code to initiate my objects:
function myObject(param){
this.content = document.createElement('div');
this.content.style.opacity = 0;
this.content.innerHTML = param;
document.body.appendChild(this.content);
this.show = function(){
if(this.content.style.opacity < 1){
this.content.style.opacity = (parseFloat(this.content.style.opacity) + 0.1).toFixed(1);
that = this;
setTimeout(function(){that.show();},100);
}
}
this.hide = function(){
if(this.content.style.opacity > 0){
this.content.style.opacity = (parseFloat(this.content.style.opacity) - 0.1).toFixed(1);
that = this;
setTimeout(function(){that.hide();},100);
}
}
}
Somewhere I have 2 objects:
obj1 = new myObject('Something here');
obj2 = new myObject('Something else here');
Somewhere in the HTML code I use them:
<button onclick="obj1.show()">Something here</button>
<button onclick="obj2.show()">Something else here</button>
When the user presses one button, everything goes OK, but if the user presses one button and after a short time interval he presses the other one, the action triggered by the first button stops and only the action of the second button is executed.
I understand that the global variable "that" becomes the refence of the second object, but I don't know how to create an automatic mechanism that wouldn't block the previously called methods.
Thank you in advance and sorry for my English if I made some mistakes :P
If you need something cancellable, use window.setInterval instead of setTimeout. setInterval returns a handle to the interval which can then be used to cancel the interval later:
var global_intervalHandler = window.setInterval(function() { ... }, millisecondsTotal);
// more code ...
// later, to cancel this guy:
window.clearInterval(global_intervalHandler);
So from here I'm sure you can use your engineering skills and creativity to make your own self expiring operations - if they execute and complete successfully (or even unsuccessfully) they cancel their own interval. If another process intervenes, it can cancel the interval first and hten fire its behavior.
There are several ways to handle something like this, here's just one off the top of my head.
First of all, I see you're writing anonymous functions to put inside the setTimeout. I find it more elegant to bind a method of my object to its scope and send that to setTimeout. There's lots of ways to do hitching, but soon bind() will become standard (you can write this into your own support libraries yourself for browser compatibility). Doing things this way would keep your variables in their own scope (no "that" variable in the global scope) and go a long way to avoiding bugs like this. For example:
function myObject(param){
// ... snip
this.show = function(){
if(this.content.style.opacity < 1){
this.content.style.opacity = (parseFloat(this.content.style.opacity) + 0.1).toFixed(1);
setTimeout(this.show.bind(this),100);
}
}
this.hide = function(){
if(this.content.style.opacity > 0){
this.content.style.opacity = (parseFloat(this.content.style.opacity) - 0.1).toFixed(1);
setTimeout(this.hide.bind(this),100);
}
}
}
Second, you probably want to add some animation-handling methods to your object. setTimeout returns handles you can use to cancel the scheduled callback. If you implement something like this.registerTimeout() and this.cancelTimeout() that can help you make sure only one thing is going on at a time and insulate your code's behavior from frenetic user clicking like what you describe.
Do you need that as global variable ? just change to var that = this; you will use variable inside of the function context.

Categories

Resources