Playing MediaStream using AudioContext.createMediaStreamSource vs HTMLAudioElement.srcObject - javascript

I'm trying to play MediaStream from remote peer (WebRTC) using Web Audio API. When i attach the stream to audio element using audio.srcObject = stream it plays ok, but when i try to use AudioContext it does not play any sound at all (I need to avoid audio/video HTML tag).
This piece works:
<audio controls>
<script>
const audioEl = document.getElementsByTagName('audio')[0];
audioEl.srcObject = MY_STREAM;
audioEl.play();
</script>
This one does not:
const audioContext = new AudioContext();
const sourceNode = audioContext.createMediaStreamSource(MY_STREAM);
sourceNode.connect(audioContext.destination);
// Trying even 'audioContext.resume()' after user gesture with no luck
What is weird about that is that when MY_STREAM is my micriphone then it plays nicely for Web Audio API (i hear the feedback from my mic).
So it would suggest that there is something different between microphone MediaStream and the one i get from WebRTC connection but why DOES it play for simple HTML audio tag?

As demonstrated by #jib, this is a Chrome bug.
I have opened a new issue to let them know about it.
I thought I found a workaround by simply assigning this MediaStream to the srcObject of a dummy HTMLAudioElement,
new Audio().srcObject = mediaStream;
but somehow, while testing on my localhost it didn't persist in time, while in this fiddle it does.
I also encountered a lot of other weird behavior while playing around, like different tabs having incidence on others and things like that.
Add to that other non-fixed bugs in the area that make me think of false positives and all-in-all, I fear there is no proper solution than waiting for them to fix it...

Related

Applying effects to MediaStream in audio tag streaming Shoutcast

I've got a project I've been working on that broke due to an update in Chrome and I've tried everything I can think of to fix it, but I think my underlying implementation is the problem.
In my project, I'm setting the src of an HTML5 audio tag to a Shoutcast link. I then capture the media stream, call createMediaStreamSource with it, then apply filter that I want to the audio (low pass filter, etc.). In Firefox, I can then call play directly on the new stream source and it plays with the given effects. In Chrome, however, since the audio element is not playing, calling play on the stream source does nothing. But if I play the audio element, Chrome plays both the audio element and the stream source. In older versions of chrome, I could just mute the audio element and the stream would continue playing, but in newer versions, this doesn't work.
Is there some better way to be doing this? I just want to play the stream source with new effects. Maybe some way of modifying the stream source with the effects, then setting that back to the audio tag? A different way of playing the streaming audio?
For additional context, I'm using a library called Pizzicato to do this. Code looks roughly like this:
audioElement.oncanplaythrough = () => {
audioElement.oncanplaythrough = null;
let capturedStream;
if (options.audioElement.mozCaptureStream) {
capturedStream = context.createMediaStreamSource(options.audioElement.mozCaptureStream());
} else {
capturedStream = context.createMediaStreamSource(options.audioElement.captureStream());
}
applyEffects(capturedStream , soundEffects); // connects effect AudioNodes together
audioStream.play();
};

createMediaElementSource() performance

I'm using the Web Audio API to analyse music played from an HTML <audio> tag using createMediaElementSource(). When I now call play()/pause() on the audio element from js I get a delay of up to a couple of seconds before anything happens. Also, when continuing to play after pausing, the audio stutters for a few secs.
My setup is as simple as it gets: A hidden <audio> created using JavaScript, an AnalyserNode attached to it, the analyser connected to the context's destination and then calling play on the Audio-Element. Before someone says it, no it's not the Analyser, it does the same thing without it.
I also noticed a bit of clipping (maybe due to stuttering?) when playing some mp3 files.
I'm using Apache Cordova, but on the Windows 10 UWP platform, so performance in general shouldn't be the problem.
Any idea why or how to circumnavigate that issue?
Try setting the preload attribute, like so;
<audio preload="auto">...</audio>
on your audio element to allow it to prebuffer a little.

Turning Youtube audio to a number

I'm trying to find a way to make a visualiser in javascript so I need to be able to get the audio output level from a youtube video and turn it into some sortof percentage or number. If anybody could help that would be great.
Note: this will only work with the HTML5 player.
You can create an AudioContext object:
context = new AudioContext()
and connect it to the YouTube video element:
src = context.createMediaElementSource($('#movie_player video'))
You can then use a ScriptProcessor to process the playing audio in real-time. The example on the Mozilla documentation page seems to provide a good base you can build on.

javascript Audio object vs. HTML5 Audio tag

In a project recently when I loaded a sound with
var myAudio = new Audio("myAudio.mp3");
myAudio.play();
It played fine unless a dialogue was opened (ie alert, confirm). However when I instead tried adding an audio tag in my html
<audio id="audio1">
<source src="alarm.mp3" type="audio/mpeg" />
</audio>
and using
var myAudio1 = document.getElementById("audio1");
myAudio1.play()
it continued to play after a dialogue was opened. Does anyone know why this is? Also more generally what are the differences between the two ways to play sounds?
According to this wiki entry at Mozilla <audio> and new Audio() should be the same but it doesn't look like that is the case in practice. Whenever I need to create an audio object in JavaScript I actually just create an <audio> element like this:
var audio = document.createElement('audio');
That actually creates an audio element that you can use exactly like an <audio> element that was declared in the page's HTML.
To recreate your example with this technique you'd do this:
var audio = document.createElement('audio');
audio.src = 'alarm.mp3'
audio.play();
JavaScript halts during an Alert or Confirm box.
You cannot concurrently run code and display an alert(), confirm(), or prompt(), it literally waits for user input on this, this is a core feature of JavaScript.
I am assuming it is that very reason why an audio file played entirely within JavaScript scope does this. Comparatively Flash video clips or HTML5 audio/video will continue to play on even when a JavaScript alert/confirm/prompt is open.
As for what method is better, well that is up to you. It is pretty archaic to do anything with the JavaScript built in alert/confirm/prompt anymore, there are way better looking prompts you can make with jQuery UI and so on.
If you have a lot of dynamic content on the page or are you looking into background buffering audio before they need to be triggered and so on, then JavaScript is probably the saner way to go about things.
If you have literally just one player on the screen then there is no excuse for not putting in onto the HTML code. Although unlikely to affect anyone these days, it is still bad practice to rely heavily on JavaScript when there is no reason to.
I came up with the function below from several answers across the web.
function playAudio(url){
var audio = document.createElement('audio');
audio.src = url;
audio.style.display = "none"; //added to fix ios issue
audio.autoplay = false; //avoid the user has not interacted with your page issue
audio.onended = function(){
audio.remove(); //remove after playing to clean the Dom
};
document.body.appendChild(audio);
}
If you will create - then you will have problems on ios, because it showing even you will set width:0px
var myAudio = new Audio("myAudio.mp3"); is faster because it does not interact with the DOM.
If you are using multiple audios and/or won't need the user to interact with the player controls you should definetly chose new Audio() where the DOM is not involved.
First let me answer the difference that lies between them.
audio tag in html and the new audio object in js, if have a difference is a subtle one and insignificant. They actually do the same thing.
If you just want to include an audio inside your webpage, then using the html tag is seem fit and recommended. And
If you would like the audio to play whilst there has been an interaction from the user, then the javascript Audio object is seem fit and recommended. For instance;
document.querySelector("button).onclick=()=>{let audio=new Audio(audio url); audio.play;
Besides that's the primary purpose of javascript.
Now the reason why the audio still plays when the dialogue opens when you use the html audio tag is because of the fact that the browser first loads your html file, execute the content of the file until it encounters the script tag in the html file and loads the javascript file too. All I'm trying to say is, the audio tag was already read by the browser even before the script loaded.
Javascript pauses when an alert(), prompt() or confirm is encountered. Thus "playing fine after an alert was opened". (•‿•).

HTML5 Audio Plays Randomly

I am implementing sound effects in HTML5 audio but after a while, it just stops playing any audio. The file type is correct because it starts fine but then seems to give up.
var sound = new Audio(url);
function play() {
sound.play();
}
Is there a better way to do this so it consistently plays sound?
Here is a link to my implementation. Easy to reproduce by pressing spacebar a lot until it eventually gives up (also shoot the lights for added sounds). http://craftyjs.com/elevatoraction/
This occurs for me in the latest version of Chrome (8.0)
Edit: I did as Gaurav suggested and only played the same instance of each sound file, but the same sort of problems are present. It will arbitrarily stop playing.
Edit 2: I just noticed that whenever I try to play the sound, the networkState is always 1 which according to this means it hasn't fully loaded. That is odd seeing as it still plays sometimes and even when it plays the networkState is always 1
Don't create a new audio object each time you want to play a sound, reuse the same resource.
var sound1 = new Audio(url);
function playSound1() {
sound1.play();
}
I think this is related to the bug http://code.google.com/p/chromium/issues/detail?id=57070

Categories

Resources