Audio sprite plays only certain parts? - javascript

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.

Related

Better way to call new Audio();

I am making game in browser and use sound effects for example shot, explosion and for every generated instance of classes there is also creating new Audio object which is eating memory so much and app is crashing after 2/3 minutes thats mean is getting very slow. Is any better way to do this? Maybe creating new Audio() in another place but just once and call it when need, not every time when generating new enemy, bullet etc.
For example:
class Bullet extends Common {
constructor() {
this.element = document.createElement("div");
this.audio = new Audio("./audio/LaserShot.wav");
}
And in upper class Spaceship I call it every time I shot pressing space:
executeShot() {
const bullet = new Bullet(this.getCurrentPosition(), this.element.offsetTop, this.area);
bullet.init();
this.bullets.push(bullet);
}
Not sure if this works great in all scenario, but you can try the following code, and see if it works.
<button class="btn">Click</button>
class AudioService {
constructor(initialsetup = 1) {
this._audios = [];
for (let i = 0; i < initialsetup; i++) {
this._audios.push(new Audio());
}
}
/**
* use to get available audio
*/
_getAudioElemToPlay() {
const audios = this._audios.filter(audio => {
// if the audio is empty, without a valid url or if the audio is ended
// TODO: not sure if these attributes are more than enough
return !audio.duration || audio.ended;
});
console.log('audios', audios);
if (audios.length == 0) {
const audio = new Audio();
this._audios.push(audio);
return audio;
}
return audios[0];
}
playAudio(url) {
const player = this._getAudioElemToPlay();
player.src = url;
player.load();
player.play();
}
}
const audioService = new AudioService();
let index = 0;
document.querySelector('.btn').addEventListener('click', function() {
index++;
const audioList = new Array(12).fill(0).map((value, index) => {
return `https://www.soundhelix.com/examples/mp3/SoundHelix-Song-${index}.mp3`;
});
audioService.playAudio(audioList[index % audioList.length]);
})
Here is the link to run the above code, https://codepen.io/hphchan/pen/xxqbezb.
You may also change the audio to other audio as you like.
My main idea to solve the issue, is by reusing the audio element created, by having an array to store it, and reuse the element once it finish playing.
Of course, for the demo, I am playing the audio by using a click button. But definitely, you can plug it into your game.
Hope the solution may help you. In case there are any cases not covering, as I have not much exposure to this area, it would be nice if you can post your modified solution here, so we can all learn together.
Have you looked at the Web Audio API? If it works for you, a single AudioBuffer can hold the audio data in memory for a given cue, and you can play it multiple times by spawning AudioBufferSourceNode objects. If you have many different sounds playing, this might not be much help, but if you are reusing sounds continuously (many laser shots), this could a big help. Another benefit is that this way of playing sounds is pretty low latency.
I just used this for the first time, getting it to work yesterday. But I'm loading it with raw PCM data (floats ranging from -1 to 1). There is surely a way to load this or an equivalent in-memory structure with a wav, but I'm too new to the API to know yet how to do this.

How to play many audio files in sequence

I have several long stories which for which the source audio is sentence-by-sentence audio files. I would like to create a web page where one could listen to individual sentences of a particular story many times, or listen to the whole story from start to finish.
To start, my web page has many <audio> elements, each of which is in a <p> with the corresponding text. Each of these <audio> elements corresponds to a single sentence of the story.
I was about to start coding up a javascript object which was going to allow some sort of "play all" functionality, you'd click the button and it would play audio[0], when that finished, audio[1], etc. It would also have a 'pause' button and keep track of where you were, etc. This way the individual sentences could still be played or enjoyed because they each have an audio element. Or, one could use my "play all" buttons at the top to treat the sequence of audio elements as if they were one big element.
Then I started asking myself if there's some better/easier/more canonical solution here. One thing I thought of doing would be to cat all of these individual audio files together into a big audio file and perhaps create 'chapter' <track> elements. Is this preferable?
What are the pros and cons to each approach? Is there some out-of-the-box solution already made for me which I simply haven't turned up?
You could use the ended event to play the next sound when the previous sound completes (Playing HTML 5 Audio sequentially via Javascript):
var sounds = new Array(new Audio("1.mp3"), new Audio("2.mp3"));
var i = -1;
playSnd();
function playSnd() {
i++;
if (i == sounds.length) return;
sounds[i].addEventListener('ended', playSnd);
sounds[i].play();
}
function playAudio(src) {
var audioElement = new Audio(src);
audioElement.play();
return audioElement
}
const sequences = ['./audio/person.m4a', './audio/4.m4a', './audio/5.m4a', './audio/6.m4a', './audio/6.m4a', './audio/6.m4a', './audio/room.m4a', './audio/3.m4a']
// play audio
let index = 0
const audioElement = playAudio(sequences[index])
audioElement.addEventListener('ended', (e) => {
index++
if (index < sequences.length) {
audioElement.src = sequences[index]
audioElement.play();
}
})
I use javascript to solve the problem, using Audio objects and a setInterval. Below an example:
var sndLetItSnow = new Audio("audio/letItSnow.m4a");
var sndSanta = new Audio("audio/snow.m4a");
var playlist = [sndLetItSnow, sndSanta];
var current = null;
var idx = 0;
function playSound() {
if (current === null || current.ended) {
// go to next
current = playlist[idx++];
// check if is the last of playlist and return to first
if (idx >= playlist.length)
idx = 0;
// return to begin
current.currentTime=0;
// play
current.play();
}
}
setInterval(playSound, 1000);
For more documentation on Audio object you can visit this page:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
I hope this help!

Javascript:: audio.currentTime is updated properly only after alert

I think that I found a bug on JavaScript Audio API.
My code is working perfectly like that :
var audio=new Audio();
var lastPause;
var myPlayer=document.querySelector('#player>.button.hand');
var playerStatus=("textContent" in document)?"textContent":"innerText";
myPlayer.addEventListener
(
'click',
function()
{
if(audio.paused)
{
audio.src="Nadav Guedj - Golden Boy (Eurovision 2015 - Israel - Karaoke).mp3";
myPlayer.innerHTML="►";
if(lastPause)
{
alert(lastPause); //If I remove this line it's not working when lastPause is defined!
audio.currentTime=lastPause;
}
audio.play();
}
else
{
myPlayer.innerHTML="||";
lastPause=audio.currentTime;
audio.pause();
}
}
);
BUT
If I remove or remark the alert line alert(lastPause); the audio stuck when lastPause is defined or iow... The second time the user press play (after pause).
I am assuming this question is related to this question my answer there is applicable for this also,
repeated setting audio.src is your problem. you have two ways of fixing this:
get rid of audio.src line, bunch of other redundant code could also be removed with it.
the reason it works when you use alert is, when alert is triggered, the next few lines are not executed till you dismiss it, in these precious few seconds/ milliseconds, the audio element whose source has been changed has enough time to load the new metadata, only then can the current time can be set. so you can modify the code to trigger setting of audio on metadata loading:
...
myPlayer.innerHTML="►";
audio.play();
audio.onloadedmetadata = function(){
if(lastPause){
audio.currentTime = lastPause;
}
};
fiddle demo

javascript: create an image resource from a byte array

I have a web page which is chock full of javascript, and a few references to resources like images for the javascript to work with. I use a websocket to communicate with the server; the javascript parses the socket's data and does things with the page presentation accordingly. It all works fine, except when it doesn't.
The problem appears to be that that page contains images which I want to display parts of, under javascript control. No matter how I play with defer, there are apparently situations in which the images don't seem to be fully downloaded before the javascript tries to use them. The result is images are missing when the page is rendered, some small percentage of the time.
I'm not very used to languages and protocols where you don't have strict control over what happens when, so the server and browser shipping stuff and executing stuff in an uncontrolled and asynch order annoys me. So I'd like to stop depending on apparently unreliable tricks like defer. What I'd like to do is just download the whole page, and then open my websocket and send my images and other resources down through it. When that process is complete, I'll know it's safe to accept other commands from the websocket and get on with doing what the page does. In other words I want to subvert the browsers asynch handling of resources, and handle it all serially under javascript control.
Pouring an image file from the server down a socket is easy and I have no trouble coming up with protocols to do it. Capturing the data as byte arrays, also easy.
But how do I get them interpreted as images?
I know there are downsides to this approach. I won't get browser caching of my images and the initial page won't load as quickly. I'm ok with that. I'm just tired of 95% working solutions and having to wonder if what I did works in every browser imaginable. (Working on everything from IE 8 to next year's Chrome is a requirement for me.)
Is this approach viable? Are there better ways to get strict, portable control of resource loading?
You still haven't been very specific about what resources you are waiting for other than images, but if they are all images, then you can use this loadMonitor object to monitor when N images are done loading:
function loadMonitor(/* img1, img2, img3 */) {
var cntr = 0, doneFn, self = this;
function checkDone() {
if (cntr === 0 && doneFn) {
// clear out doneFn so nothing that is done in the doneFn callback
// accidentally cause the callback to get called again
var f = doneFn;
doneFn = null;
f.call(self);
}
}
function handleEvents(obj, eventList) {
var events = eventList.split(" "), i;
function handler() {
--cntr;
for (i = 0; i < events.length; i++) {
obj.removeEventListener(events[i], handler);
}
checkDone();
}
for (i = 0; i < events.length; i++) {
obj.addEventListener(events[i], handler);
}
}
this.add = function(/* img1, img2, img3 */) {
if (doneFn) {
throw new Error("Can't call loadMonitor.add() after calling loadMonitor.start(fn)");
}
var img;
for (var i = 0; i < arguments.length; i++) {
img = arguments[i];
if (!img.src || !img.complete) {
++cntr;
handleEvents(img, "load error abort");
}
}
};
this.start = function(fn) {
if (!fn) {
throw new Error("must pass completion function as loadMonitor.start(fn)");
}
doneFn = fn;
checkDone();
};
// process constructor arguments
this.add.apply(this, arguments);
}
// example usage code
var cardsImage = new Image();
cardsImage.src = ...
var playerImage = new Image();
playerImage.src = ...
var tableImage = new Image();
var watcher = new loadMonitor(cardsImage, playerImage, tableImage);
// .start() tells the monitor that all images are now in the monitor
// and passes it our callback so it can now tell us when things are done
watcher.start(function() {
// put code here that wants to run when all the images are loaded
});
// the .src value can be set before or after the image has been
// added to the loadMonitor
tableImage.src = ...
Note, you must make sure that all images you put in the loadMonitor do get a .src assigned or the loadMonitor will never call its callback because that image will never finish.
Working demo: http://jsfiddle.net/jfriend00/g9x74d2j/

Chrome issue changing source of audio element when using AudioContext

The Audio sound is distorded (like playing twice at the same time or something like that) when I change its source dynamically, if the element was used in the createMediaElementSource of an AudioContext.
Here is a minimalist example of the error: http://jsfiddle.net/BaliBalo/wkFpv/ (It works well at first but it is going crazy when you click a link to change the source).
var audio = document.getElementById('music');
var actx = new webkitAudioContext();
var node, processor = actx.createScriptProcessor(1024, 1, 1);
processor.onaudioprocess = function(e) { /* STUFF */ };
processor.connect(actx.destination);
audio.addEventListener('canplay', canPlayFired);
function canPlayFired(event)
{
node = actx.createMediaElementSource(audio);
node.connect(processor);
audio.removeEventListener('canplay', canPlayFired);
}
$('a.changeMusic').click(function(e){
e.preventDefault();
audio.src = $(this).attr('href');
});
If I put node.disconnect(0); before audio.src = ... it works but the data is no more processed. I tried a lot of thing like creating a new context but it seems not to erase the previously set javascript node.
Do you know how I could fix it ?
Thanks in advance.
I would suggest taking a look at this: jsbin.com/acolet/1
It seems to be doing the same thing you are looking for.
I found this from this post.

Categories