Calling a function which is inside another function - javascript

I am trying to call a function which is inside a Json from outside it. I want to trigger the "next" function on button "onClick" method. Here is my code. I tried calling it by onClick={this.next}, but it is never being called by this method.
export default class PlayerLogic extends Component{
componentDidMount() {
var self = this;
var player = videojs(this.refs.video, this.props.options).ready(function () {
self.player = this;
self.player.on('play', self.handlePlay);
});
player.markers({
onMarkerReached: function () {
player.pause();
},
next : function() {
// go to the next marker from current timestamp
console.log("reached")
var currentTime = player.currentTime();
for (var i = 0; i < markersList.length; i++) {
var markerTime = setting.markerTip.time(markersList[i]);
if (markerTime > currentTime) {
player.currentTime(markerTime);
break;
}
}
}
});
}
render() {
return (
<div>
<video {... props}>
<source src={this.props.src} type={this.props.type} id={this.props.id}/>
</video>
<button onClick={this.next}>Next</button>
</div>)
}
};

You have to refactor it with state further as below,
export default class PlayerLogic extends Component{
constructor() {
super();
this.state = {
player : {}
};
}
componentDidMount() {
var self = this;
var player = videojs(this.refs.video, this.props.options).ready(function () {
self.player = this;
self.player.on('play', self.handlePlay);
});
player.markers({
onMarkerReached: function () {
player.pause();
},
next : function() {
// go to the next marker from current timestamp
console.log("reached")
var currentTime = player.currentTime();
for (var i = 0; i < markersList.length; i++) {
var markerTime = setting.markerTip.time(markersList[i]);
if (markerTime > currentTime) {
player.currentTime(markerTime);
break;
}
}
}
});
this.setState({ player: player });
}
next() {
this.state.player.next ();
}
render() {
return (
<div>
<video {... props}>
<source src={this.props.src} type={this.props.type} id={this.props.id}/>
</video>
<button onClick={this.next.bind(this)}>Next</button>

I understand that you are using React, right! Is this .jsx or .tsx? Why you don't use arrow function. It's an ES6 feature. You can do this:
next: () => {
...
}
Mostly, all browser are supported with ES6. You can try this in your browser console:
let foo = () => 5;
console.log(foo());
Or you can compile your code back to ES5 using babel:
https://babeljs.io/docs/learn-es2015/
The problem here is when you use function, javascript will assume that this.next is your button next method, not your player next method. Arrow function () => {} will preserve this as you did:
var self = this;
var callback = function() {
self.doSomethingHere();
}
var player = video(...).ready(callback);
So you just has to:
let callback = () => {
this.doSomethingHere();
}

Related

How to clear a sequence of audio with timeouts in React

I have set 4 timeout for audios in my application and I need to stop the audios after user click. The macro function is working correctly, however the clearTimout does not stop the sound. Anyone knows how to clear it?
export function handlePlay(audio) {
audio.currentTime = 0;
return audio.play();
}
export function handleConversation(clear) {
const timer1 = () => setTimeout(() => {
handlePlay(conversation[Math.floor(Math.random() * conversation.length)]);
}, TIME1);
const timer2 = () => setTimeout(() => {
handlePlay(conversation[Math.floor(Math.random() * conversation.length)]);
}, TIME2);
const timer3 = () => setTimeout(() => {
handlePlay(conversation[Math.floor(Math.random() * conversation.length)]);
}, TIME3);
const timer4 = () => setTimeout(() => {
handlePlay(conversation[Math.floor(Math.random() * conversation.length)]);
}, TIME4);
if (clear) {
console.log('enter clear');
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
clearTimeout(timer3);
clearTimeout(timer4);
};
}
timer1();
timer2();
timer3();
timer4();
}
after clearTimeouts call this code
audio.pause();
audio.currentTime = 0;
Here a suggestion of what you could do.
I guess this could be improved further regarding how this handleConversation function is used, I didn't really get the whole idea and there is still some inconsistencies...
function createAudio(track) {
track.audio = conversation[Math.floor(Math.random() * conversation.length)];
}
export class Track {
constructor(time) {
this.time = time;
this.timeoutid = 0;
this.audio = new Audio();
}
timer() {
this.timeoutid = setTimeout(() => {
createAudio(this);
handlePlay(this.audio);
}, TIME1);
}
play() {
this.audio.currentTime = 0;
this.audio.play();
}
stop() {
this.audio.pause();
this.audio.currentTime = 0;
clearTimeout(this.timeoutid)
}
}
export function handleConversation(clear) {
const track1 = new Track(TIME1);
const track2 = new Track(TIME2);
const track3 = new Track(TIME3);
const track4 = new Track(TIME4);
// this part actually doesn't make a lot of sense, since all tracks will be recreated each time the function is called.
// I don't really understand how this is used.
// I imagine the tracks should more likey be stored outside the function in a persistent object.
// I will modify my answer if you provide more details about how you use handleConversation
if (clear) {
console.log('enter clear');
return () => {
[track1, track2, track3, track4].forEach(track => {
track.stop()
});
};
}
[track1, track2, track3, track4].forEach(track => {
track.timer()
});
}

What's the use of `this` in this example?

I'm using a module to detect when the user is speaking, called hark. Here's some of the code:
// original source code is taken from:
// https://github.com/SimpleWebRTC/hark
// copyright goes to &yet team
// edited by Muaz Khan for RTCMultiConnection.js
function hark(stream, options) {
var audioContextType = window.webkitAudioContext || window.AudioContext;
var harker = this;
harker.events = {};
harker.on = function (event, callback) {
harker.events[event] = callback;
};
harker.emit = function () {
if (harker.events[arguments[0]]) {
harker.events[arguments[0]](arguments[1], arguments[2], arguments[3], arguments[4]);
}
};
// make it not break in non-supported browsers
if (!audioContextType) return harker;
options = options || {};
// Config
var smoothing = (options.smoothing || 0.1),
interval = (options.interval || 50),
threshold = options.threshold,
play = options.play,
history = options.history || 10,
running = true;
(...)
return harker;
}
What is this line for?
var harker = this;
When I checked in the debugger, this stores a Window object in harker. And from what I'm seeing it makes for some unexpected behavior when I call hark more than once.
Why not just do var harker;?
Full code is here:
https://www.webrtc-experiment.com/hark.js
And here's a demo where it's used:
<style>
html, body {
margin: 0!important;
padding: 0!important;
}
video {
width: auto;
max-width: 100%;
}
</style>
<title>Auto Stop RecordRTC on Silence</title>
<h1>Auto Stop RecordRTC on Silence</h1>
<br>
<button id="btn-start-recording">Start Recording</button>
<button id="btn-stop-recording" disabled style="display: none;">Stop Recording</button>
<hr>
<video controls autoplay playsinline></video>
<script src="/RecordRTC.js"></script>
<script src="https://www.webrtc-experiment.com/hark.js"></script>
<script>
var video = document.querySelector('video');
var h1 = document.querySelector('h1');
var default_h1 = h1.innerHTML;
function captureCamera(callback) {
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(camera) {
callback(camera);
}).catch(function(error) {
alert('Unable to capture your camera. Please check console logs.');
console.error(error);
});
}
function stopRecordingCallback() {
video.srcObject = null;
var blob = recorder.getBlob();
video.src = URL.createObjectURL(blob);
recorder.camera.stop();
video.muted = false;
}
var recorder; // globally accessible
document.getElementById('btn-start-recording').onclick = function() {
this.disabled = true;
captureCamera(function(camera) {
video.muted = true;
video.srcObject = camera;
recorder = RecordRTC(camera, {
type: 'video'
});
recorder.startRecording();
var max_seconds = 3;
var stopped_speaking_timeout;
var speechEvents = hark(camera, {});
speechEvents.on('speaking', function() {
if(recorder.getBlob()) return;
clearTimeout(stopped_speaking_timeout);
if(recorder.getState() === 'paused') {
// recorder.resumeRecording();
}
h1.innerHTML = default_h1;
});
speechEvents.on('stopped_speaking', function() {
if(recorder.getBlob()) return;
// recorder.pauseRecording();
stopped_speaking_timeout = setTimeout(function() {
document.getElementById('btn-stop-recording').click();
h1.innerHTML = 'Recording is now stopped.';
}, max_seconds * 1000);
// just for logging purpose (you ca remove below code)
var seconds = max_seconds;
(function looper() {
h1.innerHTML = 'Recording is going to be stopped in ' + seconds + ' seconds.';
seconds--;
if(seconds <= 0) {
h1.innerHTML = default_h1;
return;
}
setTimeout(looper, 1000);
})();
});
// release camera on stopRecording
recorder.camera = camera;
document.getElementById('btn-stop-recording').disabled = false;
});
};
document.getElementById('btn-stop-recording').onclick = function() {
this.disabled = true;
recorder.stopRecording(stopRecordingCallback);
};
</script>
<footer style="margin-top: 20px;"><small id="send-message"></small></footer>
<script src="https://www.webrtc-experiment.com/common.js"></script>
The pattern of assigning the value of this to a variable is something you can read more about by searching for this that pattern (or self = this ) for example, since that's a common name for the variable for "saving" a reference to this.
The reason for doing that is that this changes depending on the context of functions. If you assign this at a specific scope you can pass that along to other functions - since they wouldn't be able to use this, since this might mean something completely different to them.

JavaScript audio player

I'm building a simple JavaScript audiotrack controller and I don't know what is wrong with my code. I've been searching for hours here and I found a simple code that did the job. I had to change a lot of things (due to typos or code errors) and I finally got this:
var TRACKS = [
{
id: 0,
nom: 'Johnny B. Goode',
autor: 'Chuck Berry',
src: 'src/johnnybgoode.mp3',
any: '1955'
},
{
id: 1,
nom: 'For Your Love',
autor: 'The Yardbirds',
src: 'src/foryourlove.mp3',
any: '1965'
}
];
var songs = 2;
var Player = function () {
"use strict";
var currentPlaying,
trackListPos,
trackList,
source,
audio;
this.getName = function () {
return currentPlaying.nom;
};
this.setTrack = function (obj) {
currentPlaying = obj;
source.src = obj.src;
audio.load();
audio.play();
return this;
};
this.setTrackList = function (t) {
trackList = t;
trackListPos = 0;
audio = document.getElementById('myAudio');
source = document.getElementById('audioSource');
this.setTrack(trackList[trackListPos]);
return this;
};
this.play = function () {
audio.load();
audio.play();
return this;
};
this.stop = function () {
audio.pause();
audio.currentTime = 0;
return this;
};
this.pause = function () {
audio.pause();
};
this.next = function () {
if (currentPlaying.id === (songs - 1)) {
trackListPos = 0;
} else {
trackListPos += 1;
}
this.setTrack(trackList[trackListPos]);
};
};
//Initialize
var reprod = new Player();
reprod.setTrackList(TRACKS).play();
function play() {
"use strict";
reprod.play();
document.getElementById("Play").innerHTML = "Pause";
}
function seguent() {
"use strict";
reprod.next();
document.getElementById("titol").innerHTML = "<b>Títol:</b>";
}
<audio id="myAudio" controls="controls">
<source id="audioSource" src="">
Your browser does not support the audio format.
</audio>
<nav class="navegador" id="sidebar">
<div class="playerwrapper">
<div class="player">
<p id="titol"></p>
<p id="autor"></p>
<p id="any"></p>
<button type="button" onclick="seguent()">Següent</button>
<button id="Play" type="button" onclick="play()">Play</button>
</div>
</div>
</nav>
As you can see there's a couple buttons that trigger the function play() and seguent() (which means next). And they don't seem to work.
The innerHTML that changes the Play button text doesn't work, but when I remove the "reprod.play();" line it does work and changes the button content.
Can someone explain what is exactly happening with my code?
(Sorry if the post is tedious, it's my second post here and I don't know the formatting)
Thanks for letting me know I could use a console, the errors that throws are:
Uncaught TypeError: Cannot read property 'load' of null
Uncaught TypeError: Cannot set property 'src' of null
Observing your code, it seems the element which setTrackList(t) is referring to doesn't exists in the DOM. I think the element you've declared for importing this script in your HTML (the <script> tag) is preceeding the <body> with your content. You have to get sure your DOM content is loaded before calling setTrackList(t), therefore you can bind the onload event to call this, else this function will not find the element.
Try this:
var reprod = new Player();
document.addEventListener("load", function() {
reprod.setTrackList(TRACKS).play();
});
function play() {
"use strict";
reprod.play();
document.getElementById("Play").innerHTML = "Pause";
}
function seguent() {
"use strict";
reprod.next();
document.getElementById("titol").innerHTML = "<b>Títol:</b>";
}
EDIT: Maybe I understood your problem: the context of the function which construct the class (function Player()) ends immediately after the calling itself, so variables are deleted. Therefore try declaring your variables in this context.
this.audio = yourValue;
Instead of:
var audio = yourValue;
EDIT: Maybe now it's working well, try this fiddle.
Except the error because of missing of tracks, it seems working.
Here's the code of the fiddle:
var TRACKS = [
{
id: 0,
nom: 'Johnny B. Goode',
autor: 'Chuck Berry',
src: 'src/johnnybgoode.mp3',
any: '1955'
},
{
id: 1,
nom: 'For Your Love',
autor: 'The Yardbirds',
src: 'src/foryourlove.mp3',
any: '1965'
}
];
var songs = 2;
var Player = function () {
this.getName = function () {
return this.currentPlaying.nom;
};
this.setTrack = function (obj) {
this.currentPlaying = obj;
this.source.src = obj.src;
this.audio.load();
this.audio.play();
return this;
};
this.setTrackList = function (t) {
this.trackList = t;
this.trackListPos = 0;
this.audio = document.getElementById('myAudio');
this.source = document.getElementById('audioSource');
this.setTrack(this.trackList[this.trackListPos]);
return this;
};
this.play = function () {
//this.audio.load();
this.audio.play();
return this;
};
this.stop = function () {
this.audio.pause();
this.audio.currentTime = 0;
return this;
};
this.pause = function () {
this.audio.pause();
};
this.next = function () {
if (this.currentPlaying.id === (songs - 1)) {
this.trackListPos = 0;
} else {
this.trackListPos += 1;
}
this.setTrack(this.trackList[this.trackListPos]);
};
};
//Initialize
var reprod = new Player();
if (document.getElementById("myAudio"))
reprod.setTrackList(TRACKS).play();
else
window.addEventListener('load', function() {
reprod.setTrackList(TRACKS).play();
});
window.play = function() {
"use strict";
reprod.play();
document.getElementById("Play").innerHTML = "Pause";
}
window.seguent = function() {
"use strict";
reprod.next();
document.getElementById("titol").innerHTML = "<b>Títol:</b>";
}
And here's the HTML:
<audio id="myAudio" controls="controls">
<source id="audioSource" src="">
Your browser does not support the audio format.
</audio>
<nav class="navegador" id="sidebar">
<div class="playerwrapper">
<div class="player">
<p id="titol"></p>
<p id="autor"></p>
<p id="any"></p>
<button type="button" onclick="window.seguent()">Següent</button>
<button id="Play" type="button" onclick="window.play()">Play</button>
</div>
</div>
</nav>

Soundcloud stream, soundmanager2 object never starts playing on Chrome when calling play()

Using SC.stream in my react app, I'm simply trying to play a track from soundcloud API. Here is my code:
SC.initialize({
client_id: '12xxx' // my client ID
});
//[...]
console.log(this.props.track.trackId); // I get here successfully the trackId from the song I'd like to play
SC.stream('/tracks/'+this.props.track.trackId, function(track){
track.play();
console.log(track); // I successfully get the track object here. playState attribute is on 1
});
Unfortunately, the tracks never starts playing. I get no errors in the console.
Edit: the problem is only on chrome, it works perfectly on firefox and safari. I'm even more puzzled now.
Edit 2: it seems to be linked to the HTML5 player not working on Chrome: when you re-enable flash player on chrome://plugins/ by checking "always allowed to run", it works
I am not sure from the code what "track" refers to when you have track.play(). If it is the audio div then this should help.
class PlayerCtrlRender extends React.Component {
render() {
let index = this.state.playIndex;
let currentTrack = this.state.randomOn ? this.state.shuffledQueue[index] : this.props.queue[index];
let source = 'http://192.168.0.101:3950/play/' + currentTrack.location;
let title = currentTrack.title;
let progressData = {count: this.state.progressCount * 100, index: this.state.progressIndex * 100};
return (
<div id='PlayerCtrlSty' style={PlayerCtrlSty}>
<div className='FlexBox'>
<div style={timerLeftSty}>{this.state.progressIndex}</div>
<PlayerActions playing={this.state.isPlaying} clickHandler={this.clickHandler}/>
<div style={timerRightSty}>{this.state.progressCount}</div>
</div>
<JProgressBar data={progressData} position='none' />
<div id="title" style={{textAlign: 'center'}}>{title}</div>
<audio
ref="audioDiv"
src={source}
onDurationChange={this.onDurationChange}
onTimeUpdate={this.onTimeUpdate}
onEnded={this.nextSong}
/>
</div>
);
}
}
export default class PlayerCtrl extends PlayerCtrlRender {
constructor() {
super();
this.state = {
playIndex: 0,
queueLength: 1,
isPlaying: false,
progressCount: 0,
progressIndex: 0,
shuffledQueue: [{title: '', location: ''}],
randomOn: false
};
}
componentDidMount = () => {
let queue = knuthShuffle(this.props.queue.slice(0));
this.setState({queueLength: queue.length, shuffledQueue: queue});
this.refs.audioDiv.volume = .1;
}
clickHandler = (buttonid) => {
this.refs.audioDiv.autoplay = false;
switch (buttonid) {
case 'play': this.refs.audioDiv.play(); this.setState({isPlaying: true}); break;
case 'pause': this.refs.audioDiv.pause(); this.setState({isPlaying: false}); break;
case 'back': this.refs.audioDiv.currentTime = 0; break;
case 'next': this.nextSong(); break;
case 'random': this.refs.audioDiv.autoplay = this.state.isPlaying;
this.setState({randomOn: !this.state.randomOn}); break;
}
}
nextSong = () => {
this.refs.audioDiv.autoplay = this.state.isPlaying;
this.refs.audioDiv.currentTime = 0;
let newIndex = this.state.playIndex + 1;
if (newIndex == this.state.queueLength) newIndex = 0;
this.setState({playIndex: newIndex});
}
onDurationChange = () => {
let duration = this.refs.audioDiv.duration;
duration = getTime(Math.floor(duration));
this.setState({progressCount: duration})
this.setState({progressIndex: 0})
}
onTimeUpdate = () => {
let currentTime = this.refs.audioDiv.currentTime;
currentTime = getTime(Math.floor(currentTime));
this.setState({progressIndex: currentTime})
}
}

setInterval() that pauses when user leaves the tab?

Is there any method in javascript that would behave like setInterval() and would stop when user leave the tab and resume when user enter the tab again?
You can create your own API, using Visibility API to detect when the tab becomes visible or hidden, and calling native setInterval and clearInterval under the hood.
var mySetInterval, myClearInterval;
(function() {
var data = Object.create(null),
id = 0;
mySetInterval = function mySetInterval(func, time) {
data[id] = {
nativeID: setInterval(func, time),
func: func,
time: time
};
return id++;
};
myClearInterval = function myClearInterval(id) {
if(data[id]) {
clearInterval(data[id].nativeID);
delete data[id];
}
};
document.addEventListener('visibilitychange', function() {
if(document.visibilityState == 'visible')
for(var id in data)
data[id].nativeID = setInterval(data[id].func, data[id].time);
else
for(var id in data)
clearInterval(data[id].nativeID);
});
})();
var mySetInterval, myClearInterval;
(function() {
var data = Object.create(null),
id = 0;
mySetInterval = function mySetInterval(func, time) {
data[id] = {
nativeID: setInterval(func, time),
func: func,
time: time
};
return id++;
};
myClearInterval = function myClearInterval(id) {
if(data[id]) {
clearInterval(data[id].nativeID);
delete data[id];
}
};
document.addEventListener('visibilitychange', function() {
if(document.visibilityState == 'visible')
for(var id in data)
data[id].nativeID = setInterval(data[id].func, data[id].time);
else
for(var id in data)
clearInterval(data[id].nativeID);
});
})();
var log = document.getElementById('log'),
timer;
document.getElementById('start').onclick = function() {
var num = 0;
myClearInterval(timer);
timer = mySetInterval(function(){
log.innerHTML = num++;
}, 1e3);
};
<input id="start" type="button" value="Start" />
<span id="log"></span>
Note the API above should not be mixed with the native one, e.g. do not attempt to create with mySetInterval and clear with clearInterval. Therefore, the IDs returned by mySetInterval are deliberately different than the native ones.

Categories