I am using an audio-player package to play a sound locally when a node app triggers an event. When the event is triggered, a function is called with the specific file that needs to be opened as such:
playSound(fileName);
The separate function looks like this:
player = new Player('someDirectory/' + fileName + '.mp3');
player.play();
This opens and begins playing the file perfectly well, however when events trigger in quick succession, both sounds will be simultaneously playing. While I initially thought to simply add a:
player.stop();
before the 'new Player' definition, the previous instance is clearly out of scope, as the function has been called a second time. How can I stop all other instances of Player before starting playback anew?
You need to declare the player variable in the outer scope, so that every function "sees" the same player.
var player;
function playSound(fileName) {
if (player) {
player.stop();
}
player = new Player(fileName);
player.play();
}
This is called a closure.
Your player should be static, only one instance should exist and play all sounds.
function Podcast() {};
Podcast.FILE_EXTENSION = 'mp3';
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
Here we created a constructor function named Podcast with a static property called FILE_EXTENSION and a static method named download.
http://elegantcode.com/2011/01/19/basic-javascript-part-7-static-properties-and-methods/
Related
Angular: I have to mute browser tab through javascript. There are no. of audio objects in all components, So I need common script to pause app audios.
Number of audio objects in each component with play & pause operations. But now I want to add button through which I can mute the browsers tab. Also If I want then I can enable it.
My current component object like (code snippet as example):
//before onInit & constructor function
audioLogin = new Audio();
//in login page loading function
this.audioLogin.play();
Like above there are no. of such objects in all components. I have to add toogle button to on/off app sounds. For that I need to mute/unmute browsers tab on that button click.
Please suggest me script (javascript/jquery) or ideas.
Create a service which can create any media elements and keep track of it.
Use the service to create instances of media elements.
Call the service function to mute all the media created by the service.
Since as you said you have already created multiple audio/video tags. You just need to refactor a little bit to make this work.
The main thing to note here is "dont create direct new Audio()" you cannot keep track of what you created. just move the creation logic to a service and call the service method.
// Create a service class to handle media creations
class MediaService {
media = []; // will hold all media references when created
createNewAudio() {
// create a Audio instance as usual.
const audio = new Audio();
// push the reference of the instance to media array
// to perform any operation in future.
this.media.push(audio);
// return the Audio instance created above.
return audio;
}
createNewVideo() {
// create a Video instance as usual.
const video = new Video();
// push the reference of the instance to media array
// to perform any operation in future.
this.media.push(video);
// return the Video instance created above.
return video;
}
muteAllMedia() {
this.media.map(m => m.pause());
}
}
// Inject and Use the service to create any media elements
class MyComponent {
constructor(private mediaService: MediaService) { }
ngOnInit() {
//in onInit function
this.audioLogin = this.mediaService.createNewAudio();
//in login page loading function
this.audioLogin.play();
}
mute() {
this.mediaService.muteAllMedia();
}
}
I am a neophyte JS developer with a past in server-side programming.
I am creating a simple web app that allows various users to engage in live audio chatting with one another. Whenever a new user logs into an audio chat room, the following ensures they can hear everyone talking
// plays remote streams
async function playStreams(streamList) {
await Promise.all(streamList.map(async (item, index) => {
// add an audio streaming unit, and play it
var audio = document.createElement('audio');
audio.addEventListener("loadeddata", function() {
audio.play();
});
audio.srcObject = item.remoteStream;
audio.id = 'audio-stream-'+item.streamID;
audio.muted = false;
}));
}
Essentially I pass a list of streams into that function and play all of them.
Now if a user leaves the environment, I feel the prudent thing to do is to destroy their <audio> element.
To achieve that, I tried
function stopStreams(streamList) {
streamList.forEach(function (item, index) {
let stream_id = item.streamID;
let audio_elem = document.getElementById('audio-stream-'+stream_id);
if (audio_elem) {
audio_elem.stop();
}
});
}
Unfortunately, audio_elem is always null in the function above. It is not that the streamIDs are mismatched - I have checked them.
Maybe this issue has to do with scoping? I am guessing the <audio> elements created within playStreams are scoped within that function, and thus stopStreams is unable to access them.
I need a domain expert to clarify whether this is actually the case. Moreover, I also need a solution regarding how to better handle this situation - one that cleans up successfully after itself.
p.s. a similar SO question came close to asking the same thing. But their case was not numerous <audio> elements being dynamically created and destroyed as users come and go. I do not know how to use that answer to solve my issue. My concepts are unclear.
I created a global dictionary like so -
const liveStreams = {};
Next, when I play live streams, I save all the <audio> elements in the aforementioned global dictionary -
// plays remote streams
async function playStreams(streamList) {
await Promise.all(streamList.map(async (item, index) => {
// add an audio streaming unit, and play it
var audio = document.createElement('audio');
audio.addEventListener("loadeddata", function() {
audio.play();
});
audio.srcObject = item.remoteStream;
audio.muted = false;
// log the audio object in a global dictionary
liveStreams[stream_id] = audio;
}));
}
I destroy the streams via accessing them from the liveStreams dictionary, like so -
function stopStreams(streamList) {
streamList.forEach(function (item, index) {
let stream_id = item.streamID;
// Check if liveStreams contains the audio element associated to stream_id
if (liveStreams.hasOwnProperty(stream_id)) {
let audio_elem = liveStreams[stream_id];
// Stop the playback
audio_elem.pause();// now the object becomes subject to garbage collection.
// Remove audio obj's ref from dictionary
delete liveStreams.stream_id;
}
});
}
And that does it.
I have loaded an audio file into an object's property. I use it as sprite where each section has a start time and end time specified. Then I use this code to play that particular part of the audio file:
speak: function(str) {
this.vo.currentTime = 0;
var curr = {};
curr = this.sprite[str];
this.vo.currentTime = curr[0];
this.vo.volume = _data.vol[1];
this.vo.play();
var onTimeUpdate = function() {
if (this.currentTime >= curr[1]) {
this.pause();
this.load();
this.currentTime = 0;
}
};
this.vo.addEventListener('timeupdate', onTimeUpdate, false);
}
the "vo" is the audio file loaded as vo = new Audio('..file..')
The "str" is the name of the property which contains the start and end time of that part. example: if i pass aud.speak('hello'); then it plays the part of the sprite where it says "hello".
The problem: Once a part is played, most of the other parts won't play after that. I have tried everything almost. that's why you see .load(), .pause(), .currentTime = 0; as well.
I fixed the problem by reloading the audio file everytime the speak() method is called. Since the file is hosted locally, it is not a problem to reload it again, but it probably won't be the best solution for someone using hosted content.
this.vo = new Audio('..file..');
I added this line at the start of speak() method.
I have many small audio files, and I want to play these files one after another, not all of them at the same time. I have used the object Audio in javascript like this
var audio_1 = new Audio();
var audio_2 = new Audio();
var audio_3 = new Audio();
audio_1.src = "/path1";
audio_2.src = "/path2";
audio_3.src = "/path3";
Now I just need to call the function play for every object, but I need to play the audio_1 alone, and play audio_2 when the first one ended.
The solution I found is to test on the property ended of every object
audio_1.ended; // returns true when it ends playing
I found an object onended inside the audio object, I thought it's a function but it's not, can someone help me and give me the best way to solve this problem.
use addEventListener instead of assigning a function to the onended property:
audio.addEventListener('ended', function() {}); // Do
audio.onended = function() {}; // Don't
So, a IMHO dirty way is this:
audio_1.play();
audio_1.addEventListener('ended', function() {
audio_2.play();
audio_2.addEventListener('ended', function() {
audio_3.play();
};
};
So I have a function like this:
function music(song) {
var audio = new Audio("audio/" + song + ".ogg");
audio.play();
}
And the problem I'm having is that audio.play() works perfectly, but if I try audio.pause() later (after defining the same variable), it doesn't work. However, if I do this:
function music() {
var audio = document.getElementById("songname");
audio.play();
}
Then I can define the variable and later use audio.pause() to pause the track. The problem is that I want to do this within javascript because I have a lot of tracks and I don't want to create audio elements for each one.
Why doesn't the first piece of code allow me to pause the audio later? Is there any way I can let it pause the audio later? If not, are there any alternatives that involve playing the audio from javascript by passing a parameter which is the song title? I don't want to have to include the src in the html.
I suspect that jQuery might have an easy solution for this but form what I've researched I couldn't find anything.
audio only exists inside the function. if you need access to it outside the function then return the instance of the Audio object you are creating:
function getAudio(song, play) {
var audio = new Audio("audio/" + song + ".ogg");
if(play) {
audio.play();
}
return audio;
}
var theTrack = getAudio('my_song', true);
theTrack.pause(); // pause the song
theTrack.play(); // play the song
The second example refers to what i assume is an audio element which in turn exposes the functionality of the Audio object. The reason the second function works anywhere is because the DOM element is always present in the DOM and you are referencing that to get at the underlying Audio API, as opposed to a using the Audio object/API directly.
I just tested a snippet and it works as such
function music(song) {
var audio = new Audio("audio/" + song + ".ogg");
audio.play();
setTimeout(function() { audio.pause(); }, 5000);
}
You can create the audio variable before defining the function.
var audio = new Audio();
function playMusic(song) {
audio.src = "audio/" + song + ".ogg";
audio.play();
}
function pauseMusic() {
audio.pause();
}