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})
}
}
Related
I have a problem with playing audio when I press or click a button.
Drum Machine
It seems like my audio has a delay but I put a audio.currentTime = 0, so I don't know what's going on.
Here is my JS:
const $ = document.querySelectorAll.bind(document);
const sounds = [
{id:'Bass Drum', letter:'Q', src:'https://dight310.byu.edu/media/audio/FreeLoops.com/3/3/Free%20Kick%20Sample%2011-909-Free-Loops.com.mp3'}
];
let volumeVal = 0.5;
$('input[type=range]')[0].addEventListener('input', function(e) {
volumeVal = e.target.value / 100;
});
function main(url, name) {
const audio = new Audio(url);
audio.currentTime = 0;
audio.preload = "auto";
audio.volume = parseFloat(volumeVal);
audio.play();
$('.name')[0].textContent = name;
}
window.onkeydown = function(e) {
function seter(id) {
setTimeout(() => {
$(`.${id}-but`)[0].classList.add('pressed');
setTimeout(() => {
$(`.${id}-but`)[0].classList.remove('pressed');
}, 140);
}, 0);
}
switch(e.key) {
case 'q':
main(sounds[0].src, sounds[0].id);
seter('q');
break;
}
}
$('.q-but')[0].addEventListener('click', () => {
main(sounds[0].src, sounds[0].id);
});
First off, big thanks to #Seblor for providing me with the solution.
I solved my problem by creating audio node for each sound:
<audio id="bass" preload="auto" src="https://dight310.byu.edu/media/audio/FreeLoops.com/3/3/Free%20Kick%20Sample%2011-909-Free-Loops.com.mp3"></audio>
And inside my JS file I added this event listener for every button:
$('.q-but')[0].addEventListener('click', () => {
const audio = $("#bass")[0];
audio.currentTime = 0;
audio.play();
});
Of course, now I could make my code less repetitive because I am doing this for all 9 buttons. And eventually I will.
This is my first submitted question. So please go easy!
I have made a very simple run/jump game using p5.js.
Game: https://simongowing1.github.io/the-art-collector-game/
Code: https://github.com/simongowing1/the-art-collector-game
Originally the 'player' was static, but I just added an animated gif (made up of two images) so that it appears to be walking.
When I check in the browser both layers of the gif show at once, so the effect does not work.
I think this is something to do with the rendering of the image. Having checked out some other solutions on stack, I tried 'createImage()' and 'createImg()' instead of simply 'loadimage()' but both of these resulted in other elements from the site disappearing.
Anyone have any ideas? Thanks in advance.
I've pasted my code from the 'game.js' file below for reference
class Game {
constructor() {
this.backgroundImage;
this.tokenImage;
}
setup() {
this.background = new Background();
this.player = new Player();
this.tokens = [];
this.obstacles = [];
}
preload() {
this.backgroundImage = [
{ src:loadImage('assets/empty_gallery_long.jpg'), x: 0, speed: 1.5}
]
this.playerImage = loadImage('assets/Player1-walking-front.gif');
this.playerImageBk = loadImage('assets/Player1-walking-back.gif');
this.tokenImage1 = loadImage('assets/token1.png');
this.tokenImage2 = loadImage('assets/token2.png');
this.tokenImage3 = loadImage('assets/token3.png');
this.obstacleImage = loadImage('assets/Obstacle1.png');
this.backgroundMusic = createAudio('assets/01 Windowlicker.mp3');
this.jumpSound = createAudio('assets/jump.mp3');
this.saleSound = createAudio('assets/Sale.m4a');
this.breakingSound = createAudio('assets/breaking.m4a')
}
gamelogic() {
if (counter === remainingTime) {}
}
draw() {
clear();
this.background.draw();
//console.log('hello')
if (frameCount === 10 || frameCount % 500 === 0) {
this.tokens.push(new Token(this.tokenImage1))
//console.log('now');
}
this.tokens.forEach(function (token) {
token.draw();
})
this.tokens = this.tokens.filter(
(token) => {
if (token.collision(this.player))
{
console.log('got it!');
return false
} else if (token.x < 0 - token.width) {
return false
} else {
return true
}
})
this.player.draw();
if (frameCount === 300 || frameCount % 1200 === 0) {
this.obstacles.push(new Obstacle(this.obstacleImage))
//console.log('now');
}
this.obstacles.forEach(function (obstacle) {
obstacle.draw();
})
this.obstacles = this.obstacles.filter(
(obstacle) => {
if (obstacle.collision(this.player))
{
// console.log('CRASH');
return false
} else {
return true
}
})
Split that gif into frames using free online editors like ezgif
Then name all frames in order player1, player2 and so on
and write code like this :-
this.playerImage = loadAnimation('player1', 'player2', 'player3');
I know there are questions similar to this on this site that have been answered, but I haven't found any that fix this issue which is why I'm making a post.
Here is what I have tried:
Listening for events "canplay", "loadedmetadata", "canplaythrough"
Doing audio.load() on initialization
Setting audio.preload to "auto"
Testing on Chrome (it works perfectly in this browser!)
Appending the audio element to document.body on initialization
When I try to play() without the eventListener (which also doesn't work), the console says: Unhandled Promise Rejection: NotSupportedError: The operation is not supported.
Am I missing something?
The code:
var AUDIO = {
create: function(name, file, type) {
AUDIO.library[name] = {};
AUDIO.library[name].type = type;
AUDIO.library[name].audio = new Audio();
AUDIO.library[name].audio.preload = "auto";
if (type=="music") {
AUDIO.library[name].audio.loop = true;
}
AUDIO.library[name].audio.src = file;
AUDIO.library[name].audio.load();
},
play: function(name, fadeout_playing) {
if (fadeout_playing) {
for (let i=0; i<AUDIO.playing.length; i++) {
let n = AUDIO.playing[i];
AUDIO.fadeout(n);
}
}
let a = AUDIO.library[name].audio;
a.currentTime = 0;
a.addEventListener("canplaythrough", function() {
console.log("you are listening to: "+ name);
AUDIO.playing.push(name);
a.play();
}, false);
},
fadeout: function(name) {
var sound = AUDIO.library[name].audio;
var fade = setInterval(function() {
if (sound.volume >= 0.1) {
sound.volume -= 0.1;
sound.volume = sound.volume.toFixed(1);
}
if (sound.volume <= 0) {
sound.volume = 0;
let index = AUDIO.playing.indexOf(name);
AUDIO.playing.splice(index, 1);
clearInterval(fade);
}
}, 200);
},
library: {},
playing: [],
};
AUDIO.create("act1", "music/chillelectric.mp3", "music");
//this runs when i click a button
function setup() {
...
AUDIO.play("act1");
}
Thanks in advance.
I made a custom audio player in React that updates the so called "Scrubber" of the audio file (progressbar) with the value based on duration of the player. Everything works fine except that if you press the play/pause button when the interval updates (mousedown -> interval updates state -> mouseup) then the button click doesn't fire.
import React, { Component } from 'react';
import ReactSVG from 'react-svg';
import VolumeButton from 'components/VolumeButton/VolumeButton.jsx';
import play from 'img/icons/play.svg';
import pause from 'img/icons/pause.svg';
import './AudioPlayer.css';
class AudioPlayer extends Component {
constructor(props) {
super(props);
this.state = {
currentTime: '00:00',
endTime: '00:00',
intervalsInitiated: false,
player: null,
playing: false,
progressBar: null
};
this.handleResize = this.handleResize.bind(this);
}
initIntervals = () => {
let intervals = setInterval(() => {
this.updateScrubber(this.state.player.currentTime);
this.updateTime(this.state.player.currentTime);
}, 100);
this.setState({
intervals: intervals,
intervalsInitiated: true
});
};
setUpAudioTime = player => {
console.log('setup time');
let playerCurrentTime = player.currentTime;
let currentTime = this.calculateTime(playerCurrentTime);
this.setState({
currentTime: currentTime,
endTime: this.calculateTime(player.duration)
});
this.updateScrubber(playerCurrentTime);
};
initPlayer = () => {
console.log('initPlayer');
// Add resize listener for showing volume on desktop
this.addResize();
this.handleResize();
// Setup correct player
let player = new Audio(this.props.audioPath);
let progressBar = document.querySelector('.ProgressBar');
// Add event for audio stopped for play button
player.addEventListener('ended', () => this.toggleAudioPlay());
// Set state to correct player
// TODO: Do this in update aswell going from one to another
this.setState({
player: player,
progressBar: progressBar
});
player.addEventListener('loadedmetadata', () =>
this.setUpAudioTime(player)
);
};
toggleAudioPlay = () => {
console.log('toggling audio');
if (!this.state.player) {
return;
}
!this.state.intervalsInitiated
? this.initIntervals()
: this.resetComponent();
this.state.playing
? this.state.player.pause()
: this.state.player.play();
this.setState({
playing: !this.state.playing
});
};
updateScrubber = playerCurrentTime => {
if (!this.state.player || !this.state.progressBar) {
return;
}
let prog = this.state.progressBar;
prog.value = playerCurrentTime / this.state.player.duration;
this.setState({
progressBar: prog
});
};
updateTime = playerCurrentTime => {
this.setState({
currentTime: this.calculateTime(playerCurrentTime)
});
};
calculateTime = lengthInSeconds => {
let minutes = Math.floor(lengthInSeconds / 60);
let seconds = Math.floor(lengthInSeconds) - minutes * 60;
let time =
(minutes < 10 ? '0' + minutes : minutes) +
':' +
(seconds < 10 ? '0' + seconds : seconds);
return time;
};
seek = event => {
if (!this.state.player || !this.state.progressBar) {
return;
}
let percent =
event.nativeEvent.offsetX / this.state.progressBar.offsetWidth;
let prog = this.state.progressBar;
prog.value = percent;
this.setState({ progressBar: prog });
let newTime = percent * this.state.player.duration;
let player = this.state.player;
player.currentTime = newTime;
this.setState({
player: player
});
this.updateTime(newTime);
};
resetComponent() {
console.log('reset component');
clearInterval(this.state.intervals);
this.setState({
intervalsInitiated: false
});
}
componentDidMount() {
this.initPlayer();
}
render() {
return (
<div
className={`AudioPlayer ${
this.state.width < 800 ? 'AudioPlayer--extended' : ''
}`}
>
<audio className="MarkerAudio" preload="metadata">
<source src={this.props.audioPath} type="audio/mp3" />
Your device doesnt support audio format.
</audio>
<div className="PlayerControls">
<div className="PlayButton" onClick={this.toggleAudioPlay}>
{this.state.playing ? (
<ReactSVG
className="AudioIcon"
svgStyle={{ height: '100%', width: '100%' }}
path={pause}
/>
) : (
<ReactSVG
className="AudioIcon"
svgStyle={{
height: '100%',
left: '2px',
position: 'relative',
width: '100%'
}}
path={play}
/>
)}
</div>
<span className="CurrentTime">
{this.state.currentTime}
</span>
<progress
onClick={this.seek}
className="ProgressBar"
value="0"
min="0"
max="1"
/>
<span>{this.state.endTime}</span>
</div>
</div>
);
}
}
export default AudioPlayer;
However, the click still works if I make sure I do a "fast-click" or if the interval isn't initiated. Any ideas on how to handle this? Should I use a onMouseDown or something instead? Or is there anyway to make sure that the interval and state change doesn't interfere with the onClick?
So for anyone wondering it had something do to with click on the icon not bubbling properly when state updated. All I had to do to fix this was to add pointer-events: none; on the icon so the click gets triggered directly on the div and not on icon.
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();
}