Random audio player integrated with mute button - html5-audio

Forgive my ignorance.
I am on the steep end of the learning curve for js (and I am 61 years old so the synapses are not firing like they used to).
I have used this code provided by an expert on this site to play a random selection from a list of audio files:
// JavaScript Document
var collection=[];// final collection of sounds to play
var loadedIndex=0;// horrible way of forcing a load of audio sounds
// remap audios to a buffered collection
function init(audios) {
for(var i=0;i<audios.length;i++) {
var audio = new Audio(audios[i]);
collection.push(audio);
buffer(audio);
}
}
// did I mention it's a horrible way to buffer?
function buffer(audio) {
if(audio.readyState==4)return loaded();
setTimeout(function(){buffer(audio)},100);
}
// check if we're leady to dj this
function loaded() {
loadedIndex++;
if(collection.length==loadedIndex)playLooped();
}
// play and loop after finished
function playLooped() {
var audio=Math.floor(Math.random() * (collection.length));
audio=collection[audio];
audio.play();
setTimeout(playLooped,audio.duration*1000);
}
// the songs to be played!
init([
'audio/song1.mp3', 'audio/song2.mp3', 'audio/song3.mp3', 'audio/song4.mp3', 'audio/song5.mp3'
]);
And I have used this code in a separate trial to add a mute button for a single audio file to avoid annoying visitors to the site if they are not hip to the music:
// JavaScript Document
$("audio").prop('muted', false);
$(".mute-audio").click(function () {
if ($("audio").prop('muted')) {
$("audio").prop('muted', false);
$(this).addClass('unmute-audio');
} else {
$("audio").prop('muted', true);
$(this).removeClass('unmute-audio');
}
console.log($("audio").prop('muted'))
});
What I cannot figure out how to accomplish is integrating the MUTE function with the RANDOM AUDIO PLAY function.
Thanks to anyone willing to give me a little guidance!

Related

How to make a close button for a Flash banner ad with Actionscript 3

I am working on a Flash banner ad built with Actionscript 3. It will be embedded into web pages.
The ad needs to have a close button. When the user clicks the button the ad should disappear.
This needs to be done entirely in the banner, as we have no control over the webpages where it will be embedded.
In Actionscript 2 I used to do this by running this code when the user clicked the button:
unloadMovie(this);
This seems to no longer work in Actionscript 3. What can I do to achieve this functionality in AS3?
Thanks for your help.
For documentation purposes, here are the conclusions Andreyu and I reached regarding the issue: unloading/removing a swf file from within the swf to allow users access to the elements under the swf.
One option is to use ExternalInterface to inject js code to:
get the id/name of the swf as registered with the DOM
remove the swf element from the DOM using the found id/name
In terms of code, this is using a technique described on Zeh Fernando's blog
as modified by Andreyu to include the DOM element removal:
// Based on work by Zeh Fernando: http://zehfernando.com/2011/getting-the-swfs-html-objectembed-id-from-within-the-flash-movie-itself/
function getSWFObjectName(): String {
// Returns the SWF's object name for getElementById
// Based on https://github.com/millermedeiros/Hasher_AS3_helper/blob/master/dev/src/org/osflash/hasher/Hasher.as
var js:XML;
js = <script><![CDATA[
function(__randomFunction) {
var check = function(objects){
for (var i = 0; i < objects.length; i++){
if (typeof(eval("objects["+i+"]." + __randomFunction)) != undefined) {
return objects[i].id;
}
}
return undefined;
};
return check(document.getElementsByTagName("object")) || check(document.getElementsByTagName("embed"));
}
]]></script>;
var __randomFunction:String = "checkFunction_" + Math.floor(Math.random() * 99999); // Something random just so it's safer
ExternalInterface.addCallback(__randomFunction, getSWFObjectName); // The second parameter can be anything, just passing a function that exists
return ExternalInterface.call(js, __randomFunction);
}
// Function to remove the SWF from the webpage
function destroyEverything(event:MouseEvent): void {
var js:XML;
js = <script><![CDATA[
function(__SWFContext) {
var element = document.getElementById(__SWFContext);
element.parentNode.removeChild(element);
}
]]></script>;
ExternalInterface.call(js, getSWFObjectName());
}
// Add function to click event of button
close_button.addEventListener(MouseEvent.CLICK, destroyEverything);
If unloadMovie was enough for you, you could simply remove everything from the stage:
//In button click handler, call "removeEverything" function
//function onClickClose(e:MouseEvent):void{
// removeEverything()
//}
//As a result you will get empty stage
function removeEverything():void {
while (stage.numChildren) {
stage.removeChildAt(0);
}
}

Switch language (audio and lyric) seamlessly in Adobe Flash

I’m creating a multilingual flash game with multilingual narrations. Till now i’ve got one language with an audio stream and lyric to accompany it in it’s own timeline controlled by a button on the main timeline to pause and play. I would like to add 2 more languages with audio and own lyric(karaoke style) for each language in this scene. And eventually have buttons on the main timeline that would switch the language(audio and lyric) and seamlessly continue from where the last language left off. Till now I have this action from the main timeline controlling the audio and lyric. englyr being the movie clip, with audio and lyric in it.
toggleButton.addEventListener(MouseEvent.CLICK, toggleClick3);
toggleButton.buttonState = "off";
function toggleClick3(event:MouseEvent) {
if (toggleButton.buttonState == "on") {
englyr.play();
toggleButton.buttonState = "off";
} else {
toggleButton.buttonState = "on";
englyr.stop();
}
}
I’m assuming I should put the other 2 languages as well as their lyric in englyr so that I can disable/mute languages that are not needed to be heard or seen. One problem is I can’t group the lyric and the narration(2 layers) together as a movie clip in that timeline. Therefore cannot disable the 2 other languages that shouldn’t be heard or seen. Any solutions?
It's probably easier to let them both play from code instead of via the timeline.
The first thing to do is to go to the settings of your audioclips in the library, enable "Export for Actionscript" and set a different class name for both your clips. I have named mine "english" and "french".
The following code manages two sounds and changes the language when you press the button of a language that is currently not playing.
var englishClip:Sound = new english(); //load both sounds.
var frenchClip:Sound = new french();
//create the sound and the sound channel.
var myChannel:SoundChannel = new SoundChannel();
var mySound:Sound = englishClip;
//if you want to have lots of different languages it might be easier to just have different buttons instead of one with a state.
englishButton.addEventListener(MouseEvent.CLICK, SpeakEnglish);
frenchButton.addEventListener(MouseEvent.CLICK, SpeakFrench);
//we'll start with having just the english sound playing.
myChannel = mySound.play();
function SpeakEnglish(event:MouseEvent) {
if (mySound != englishClip) { //if the english sound is already playing, do nothing.
var currentPlayPosition:Number = myChannel.position; //save playback position.
myChannel.stop(); //stop playing
mySound = englishClip.play(currentPlayPosition); //resume playing from saved position.
}
function SpeakFrench(event:MouseEvent) {
if (mySound != frenchClip) { //if the French sound is already playing, do nothing.
var currentPlayPosition:Number = myChannel.position; //save playback position.
myChannel.stop(); //stop playing
mySound = frenchClip.play(currentPlayPosition); //resume playing from saved position.
}
This could all be made more compact by having a single function that you pass the appropriate sound to. It would look something like this:
function SpeakEnglish(event:MouseEvent) {
ChangeSound(englishClip);
}
function SpeakFrench(event:MouseEvent) {
ChangeSound(frenchClip);
}
function ChangeSound(newSound:Sound){
if (mySound != newSound) { //if the sound is already playing, do nothing.
var currentPlayPosition:Number = myChannel.position; //save playback position.
myChannel.stop(); //stop playing
mySound = newSound.play(currentPlayPosition); //resume playing from saved
}
And that should solve the problem, i hope that helped :)
Resource: http://www.republicofcode.com/tutorials/flash/as3sound/

HTML5 Audio stop function

I am playing a small audio clip on click of each link in my navigation
HTML Code:
<audio tabindex="0" id="beep-one" controls preload="auto" >
<source src="audio/Output 1-2.mp3">
<source src="audio/Output 1-2.ogg">
</audio>
JS code:
$('#links a').click(function(e) {
e.preventDefault();
var beepOne = $("#beep-one")[0];
beepOne.play();
});
It's working fine so far.
Issue is when a sound clip is already running and i click on any link nothing happens.
I tried to stop the already playing sound on click of link, but there is no direct event for that in HTML5's Audio API
I tried following code but it's not working
$.each($('audio'), function () {
$(this).stop();
});
Any suggestions please?
Instead of stop() you could try with:
sound.pause();
sound.currentTime = 0;
This should have the desired effect.
first you have to set an id for your audio element
in your js :
var ply = document.getElementById('player');
var oldSrc = ply.src;// just to remember the old source
ply.src = "";// to stop the player you have to replace the source with nothing
I was having same issue. A stop should stop the stream and onplay go to live if it is a radio. All solutions I saw had a disadvantage:
player.currentTime = 0 keeps downloading the stream.
player.src = '' raise error event
My solution:
var player = document.getElementById('radio');
player.pause();
player.src = player.src;
And the HTML
<audio src="http://radio-stream" id="radio" class="hidden" preload="none"></audio>
Here is my way of doing stop() method:
Somewhere in code:
audioCh1: document.createElement("audio");
and then in stop():
this.audioCh1.pause()
this.audioCh1.src = 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAVFYAAFRWAAABAAgAZGF0YQAAAAA=';
In this way we don`t produce additional request, the old one is cancelled and our audio element is in clean state (tested in Chrome and FF) :>
This method works:
audio.pause();
audio.currentTime = 0;
But if you don't want to have to write these two lines of code every time you stop an audio you could do one of two things. The second I think is the more appropriate one and I'm not sure why the "gods of javascript standards" have not made this standard.
First method: create a function and pass the audio
function stopAudio(audio) {
audio.pause();
audio.currentTime = 0;
}
//then using it:
stopAudio(audio);
Second method (favoured): extend the Audio class:
Audio.prototype.stop = function() {
this.pause();
this.currentTime = 0;
};
I have this in a javascript file I called "AudioPlus.js" which I include in my html before any script that will be dealing with audio.
Then you can call the stop function on audio objects:
audio.stop();
FINALLY CHROME ISSUE WITH "canplaythrough":
I have not tested this in all browsers but this is a problem I came across in Chrome. If you try to set currentTime on an audio that has a "canplaythrough" event listener attached to it then you will trigger that event again which can lead to undesirable results.
So the solution, similar to all cases when you have attached an event listener that you really want to make sure it is not triggered again, is to remove the event listener after the first call. Something like this:
//note using jquery to attach the event. You can use plain javascript as well of course.
$(audio).on("canplaythrough", function() {
$(this).off("canplaythrough");
// rest of the code ...
});
BONUS:
Note that you can add even more custom methods to the Audio class (or any native javascript class for that matter).
For example if you wanted a "restart" method that restarted the audio it could look something like:
Audio.prototype.restart= function() {
this.pause();
this.currentTime = 0;
this.play();
};
It doesn't work sometimes in chrome,
sound.pause();
sound.currentTime = 0;
just change like that,
sound.currentTime = 0;
sound.pause();
From my own javascript function to toggle Play/Pause - since I'm handling a radio stream, I wanted it to clear the buffer so that the listener does not end up coming out of sync with the radio station.
function playStream() {
var player = document.getElementById('player');
(player.paused == true) ? toggle(0) : toggle(1);
}
function toggle(state) {
var player = document.getElementById('player');
var link = document.getElementById('radio-link');
var src = "http://192.81.248.91:8159/;";
switch(state) {
case 0:
player.src = src;
player.load();
player.play();
link.innerHTML = 'Pause';
player_state = 1;
break;
case 1:
player.pause();
player.currentTime = 0;
player.src = '';
link.innerHTML = 'Play';
player_state = 0;
break;
}
}
Turns out, just clearing the currentTime doesn't cut it under Chrome, needed to clear the source too and load it back in. Hope this helps.
As a side note and because I was recently using the stop method provided in the accepted answer, according to this link:
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events
by setting currentTime manually one may fire the 'canplaythrough' event on the audio element. In the link it mentions Firefox, but I encountered this event firing after setting currentTime manually on Chrome. So if you have behavior attached to this event you might end up in an audio loop.
shamangeorge wrote:
by setting currentTime manually one may fire the 'canplaythrough' event on the audio element.
This is indeed what will happen, and pausing will also trigger the pause event, both of which make this technique unsuitable for use as a "stop" method. Moreover, setting the src as suggested by zaki will make the player try to load the current page's URL as a media file (and fail) if autoplay is enabled - setting src to null is not allowed; it will always be treated as a URL. Short of destroying the player object there seems to be no good way of providing a "stop" method, so I would suggest just dropping the dedicated stop button and providing pause and skip back buttons instead - a stop button wouldn't really add any functionality.
This approach is "brute force", but it works assuming using jQuery is "allowed". Surround your "player" <audio></audio> tags with a div (here with an id of "plHolder").
<div id="plHolder">
<audio controls id="player">
...
</audio>
<div>
Then this javascript should work:
function stopAudio() {
var savePlayer = $('#plHolder').html(); // Save player code
$('#player').remove(); // Remove player from DOM
$('#FlHolder').html(savePlayer); // Restore it
}
I was looking for something similar due to making an application that could be used to layer sounds with each other for focus. What I ended up doing was - when selecting a sound, create the audio element with Javascript:
const audio = document.createElement('audio') as HTMLAudioElement;
audio.src = getSoundURL(clickedTrackId);
audio.id = `${clickedTrackId}-audio`;
console.log(audio.id);
audio.volume = 20/100;
audio.load();
audio.play();
Then, append child to document to actually surface the audio element
document.body.appendChild(audio);
Finally, when unselecting audio, you can stop and remove the audio element altogether - this will also stop streaming.
const audio = document.getElementById(`${clickedTrackId}-audio`) as HTMLAudioElement;
audio.pause();
audio.remove();
If you have several audio players on your site and you like to pause all of them:
$('audio').each( function() {
$(this)[0].pause();
});
I believe it would be good to check if the audio is playing state and reset the currentTime property.
if (sound.currentTime !== 0 && (sound.currentTime > 0 && sound.currentTime < sound.duration) {
sound.currentTime = 0;
}
sound.play();
for me that code working fine. (IE10+)
var Wmp = document.getElementById("MediaPlayer");
Wmp.controls.stop();
<object classid="clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6"
standby="Loading áudio..." style="width: 100%; height: 170px" id="MediaPlayer">...
Hope this help.
What I like to do is completely remove the control using Angular2 then it's reloaded when the next song has an audio path:
<audio id="audioplayer" *ngIf="song?.audio_path">
Then when I want to unload it in code I do this:
this.song = Object.assign({},this.song,{audio_path: null});
When the next song is assigned, the control gets completely recreated from scratch:
this.song = this.songOnDeck;
The simple way to get around this error is to catch the error.
audioElement.play() returns a promise, so the following code with a .catch() should suffice manage this issue:
function playSound(sound) {
sfx.pause();
sfx.currentTime = 0;
sfx.src = sound;
sfx.play().catch(e => e);
}
Note: You may want to replace the arrow function with an anonymous function for backward compatibility.
In IE 11 I used combined variant:
player.currentTime = 0;
player.pause();
player.currentTime = 0;
Only 2 times repeat prevents IE from continuing loading media stream after pause() and flooding a disk by that.
What's wrong with simply this?
audio.load()
As stated by the spec and on MDN, respectively:
Playback of any previously playing media resource for this element stops.
Calling load() aborts all ongoing operations involving this media element

Play sound at certain playProgress or videoTime with greensock?

I'm using greensock LoaderMax to load video files and sound files. I've copied as much code as is available to me. A video (s9) is playing and at a certain percentage through the video, I need to play another sound.
if(s9.playProgress > .1) // This is what I can't get to work
{
s12_sound.playSound(); //This sound won't play at .1 playProgress
}
s9.content.visible = true;
s9.playVideo();
stop();
s9.addEventListener(VideoLoader.VIDEO_COMPLETE, play_s9_loop); //This plays a video once s9 is done.
function play_s9_loop(event:Event):void
{
s9.content.visible = false;
s9_loop.content.visible = true;
s9_loop.playVideo();
}
I'm guessing you just can't do an if() on playProgress? Furthermore, I suck at AS3.
You should be able to just listen for the INIT event on the video (which typically means it has loaded enough to determine the duration of the video) and then add an AS cue point.
//...after you create your VideoLoader...
myVideoLoader.addEventListener(LoaderEvent.INIT, initHandler);
myVideoLoader.load();
function initHandler(event:LoaderEvent):void {
myVideoLoader.addASCuePoint( myVideoLoader.duration * 0.1, "myLabel" );
myVideoLoader.addEventListener(VideoLoader.VIDEO_CUE_POINT, cuePointHandler);
}
function cuePointHandler(event:LoaderEvent):void {
trace("Hit the cue point " + event.data.name);
s12_sound.playSound();
}
Also make sure that you preload that s12_sound so that it's ready to play when you need it. Otherwise, you can call playSound() all you want and it ain't gonna happen :)
I haven't used this class before but after reading the docs it looks like you can do something like this:
http://www.greensock.com/as/docs/tween/com/greensock/loading/VideoLoader.html
var mid:Number = s9_loop.duration/2; //get the midpoint using the duration property
s9_loop.addASCuePoint(mid, "middle") //using addASCubePoint to add a cuepoint to the midpoint of the video
s9_loop.addEventListener(VideoLoader.VIDEO_CUE_POINT, handleMidpoint); //listen for the cuepoint
Inside the handler function
protected function handleMidpoint(e:Event):void{
//play your sound
}

How to do something after video finishes?

I have a 15 second video that's not embedded (it's sourced from an url). How can I get it to play the next frame when then video is finished?
If you are using the FLVPlayback component, you can do it like this:
// for clarification
var video:FLVPlayback = myVideoInstance;
video.source = "http://example.com/myvideo.flv";
video.autoPlay = true;
video.addEventListener(VideoEvent.COMPLETE, function(e:VideoEvent) {
// video has finished
});
See VideoEvent.COMPLETE which is dispatched when a video has finished playing.
video.addEventListener(VideoEvent.COMPLETE, _doNext);
function _doNext(e:VideoEvent):void
{
video.removeEventListener(VideoEvent.COMPLETE, _doNext);
trace("video done, what's next?");
}