AS3 playing audio and looping it - actionscript-3

I'm trying to get an audio file to play in the background of my project but so far have been unsuccessful so far. Heres what I've gotten so far.
package {
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.media.*;
public class Music extends MovieClip {
// Create a sound
private var music:Sound;
// Use a URLRequest for file path
private var request:URLRequest;
// Create a sound buffer
private var buffer:SoundLoaderContext;
// Create a sound channel
private var channel:SoundChannel;
public function Music() {
// constructor code
//Instantiate all sound objects
buffer = new SoundLoaderContext(1000);
request = new URLRequest("SMB3-Grassland-Map.mp3");
music = new Sound(request, buffer);
// Play music and assign it to a channel
channel = music.play();
// Add an event listener to the channel
channel.addEventListener(Event.SOUND_COMPLETE, loopMusic);
}
// playMusic method restarts music
private function playMusic()
{
channel = music.play();
channel.addEventListener(Event.SOUND_COMPLETE, loopMusic);
}
// Channel event listener call playMusic method
private function loopMusic(e:Event)
{
if (channel != null)
{
channel.removeEventListener(Event.SOUND_COMPLETE, loopMusic);
playMusic();
}
}
}
}
this is just to play an external audio file and have it continually loop.

Rather than having an event listener that plays the sound again upon sound complete, you can add parameters to the .play() method. The first parameter indicates at what point in the sound you would like it to begin playing, the second parameter indicates how many times you would like it to play, and the third is used if you want to apply sound transform. In your case you can do .play(0, int.MAX_VALUE); this will give you the continuous loop you are looking for.

"the problem is it's not playing anything at all."
I copied your code and created an AS3 class file (substituting my own local MP3 URL)
Then created an AS3 file with the following:
import Music;
var mus:Music = new Music();
and ran it. The sound played and looped correctly.
It's working for me, so maybe that's why there are no errors. Sorry for the question but are your speakers on; is your system playing sound from another source... cd, mp3 player?
I've used the play(0, 1000); #Goose mentioned and it worked great; simple and effective.

Related

How to STOP looping sound when going into next frame/Errors

I have a flash project broken up into multiple frames, with a button on each frame that goes to play the next frame. (And a movieclip on each frame that plays until you hit next frame button)
On each frame, I want audio to play, and loop.
But, I want the audio from one frame to stop when I click the button to go to the next.
On frame 4, I have this code:
import flash.media.SoundChannel;
var sound:Sound = new firt2();
var soundChannel:SoundChannel;
sound.addEventListener(Event.COMPLETE, onSoundLoadComplete);
sound.play();
function onSoundLoadComplete(e:Event):void{
sound.removeEventListener(Event.COMPLETE, onSoundLoadComplete);
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundChannelSoundComplete);
}
function onSoundChannelSoundComplete(e:Event):void{
e.currentTarget.removeEventListener(Event.SOUND_COMPLETE, onSoundChannelSoundComplete);
}
And it works. However, I want to stop it once I click the button to go to the next frame. I have tried:
soundChannel.stop();
On the next frame.
However, whenever I do that, the output reads:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at hhh4_fla::MainTimeline/frame5()
at flash.display::MovieClip/gotoAndPlay()
at hhh4_fla::MainTimeline/fl_ClickToGoToAndPlayFromFrame()
All of my buttons and movieclip have instance names.
Rather than figuring why it doesn't work with all these frames and timelines, I think it's better to compose a centralized sound manager class that handles these things.
Implementation. Keep in mind that I didn't test that so please excuse me for occasional typo if any. The logic of it all should be correct.
package
{
import flash.system.ApplicationDomain;
import flash.media.SoundChannel;
import flash.media.Sound;
import flash.events.Event;
public class Audio
{
// Container to cache Sound objects.
static private const cache:Object = new Object;
// Variables to hold the current values.
static private var currentChannel:SoundChannel;
static private var currentSound:String;
// Stops the current sound playing. If you pass the sound name, it
// will stop the audio track only if it is the exact one playing.
// Otherwise it will stop any one currently playing.
static public function stop(value:String = null):void
{
// Do nothing if nothing is playing right now,
// or if the specific sound requested to stop does not match.
if (currentSound == null) return;
if (value) if (value != currentSound) return;
// Unsubscribe from event and stop the audio.
currentChannel.removeEventListener(Event.SOUND_COMPLETE, onComplete);
currentChannel.stop();
// Final clean-up.
currentChannel = null;
currentSound = null;
}
// Plays the embedded sound by its class name.
static public function play(value:String):void
{
// Do nothing if the requested sound is already playing.
if (value == currentSound) return;
// Stop the current audio track playing.
stop();
// Check if that one sound is valid and/or was previously requested.
if (!cache[value])
{
try
{
// Obtain class definition from the project.
var aClass:Class = ApplicationDomain.currentDomain.getDefinition(value) as Class;
// Try instantiating the Sound.
if (aClass) cache[value] = new aClass as Sound;
}
catch (fail:Error)
{
// Well, do nothing, yet.
}
}
if (cache[value])
{
// Store the id of audio track that is going to be playing.
currentSound = value;
// Play the track and subscribe to it for the SOUND_COMPLETE event.
currentChannel = (cache[value] as Sound).play();
currentChannel.addEventListener(Event.SOUND_COMPLETE, onComplete);
}
else
{
// If there's no such class, or it is not a Sound,
trace("ERROR: there's no sound <<" + value + ">> is embedded into the project.");
}
}
// Event handler to clean up once the current audio track is complete.
static private function onComplete(e:Event):void
{
// Sanity check.
if (e.target != currentChannel) return;
stop();
}
}
}
Usage.
import Audio;
// Any time you want different sound to play.
// Pass the class name as Sting as an argument.
Audio.play("firt2");
// Any time you just want to stop the sound;
Audio.stop();

ActionScript 3.0 sound not working

So having trouble making sound on keyboard press
I have the imports:
import flash.net.URLRequest;
import flash.media.Sound;
I have the variables
private var soundDownRequest:URLRequest = new URLRequest ("SoundDown.mp3");
private var downSound:Sound = new Sound (soundDownRequest);
and the event listener
private function keyDownHandler(evt:KeyboardEvent):void
{
if (evt.keyCode == 40)//ascii for down arrow
{
downSound.play();
}
}
The sound folder is in the same folder as the .as, its also in the library of the fla, yet it still doesn't work. Any idea why?
Thank you.
Update:
I got the sound to work but not using the external method I was trying to do above.
Had to do it internally.
so you need:
import flash.media.SoundChannel;
-Then you need to make sure your sound file is in your fla library.
once its in the library
-Right click > properties
-Select the Action Script Tab
-Check "export for action script"
-Give the class a name in accordance to the sound
-press ok
add this variable (your will be different):
private var downSound:TheDownSound = new TheDownSound();
downsound is the selected name of the variable, and TheDownSound is the name of the class (the one made earlier for the sound file)
then add this to where you want the sound to play:
var myDownSound:SoundChannel = downSound.play();
Do this if you cant get it working externally like me.
for a better explanation watch this guys youtube video:
https://www.youtube.com/watch?v=SZpwppe7yGs
Your code is working perfectly ok if you put your .mp3 file in the same folder as the output .swf, not near the class .as source file (because its the swf file loading the sound, so the path must be relative to it)
public class ASEntryPoint extends Sprite {
private var soundDownRequest:URLRequest = new URLRequest ("click.mp3");
private var downSound:Sound = new Sound (soundDownRequest);
public function ASEntryPoint() {
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
}
private function keyDownHandler(evt:KeyboardEvent):void{
if (evt.keyCode == 40) {
downSound.play();
}
}
}
You need to load the external file, which is asynchronous operation. Then you track the loading event and if it all goes normally you can play your loaded sound.
import flash.events.SecurityErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.Event;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.media.SoundChannel;
// Keep the sound loader from being garbage collected.
var soundLoader:Sound;
function loadSound(url:String):void
{
var aRequest:URLRequest = new URLRequest(url);
soundLoader = new Sound();
// Handle the normal loading.
soundLoader.addEventListener(Event.COMPLETE, onLoaded);
// Handle the error cases.
soundLoader.addEventListener(IOErrorEvent.IO_ERROR, onError, false, 0, true);
soundLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError, false, 0, true);
soundLoader.load(aRequest);
}
var audioChannel:SoundChannel;
function onLoaded(e:Event):void
{
// Sound is available here for playback.
audioChannel = soundLoader.play();
}
function onError(e:Event):void
{
trace(e);
}
You can also handle your sound as a streaming audio, but I worked with that years ago in AS2 so I cannot help here. Still, internet suggests a link: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7d22.html

AS3 SoundChannel Position of audio in function

I am having issues with variable scope. I know this must be an easy fix but I can't find it.
I need to play sound through a sound channel in a function. Then I need another function to read the sound channel position. But it is not seeing it because of the new var in the function that plays it. If I play it outside the function I can get the position from another function but if I beging the sound in the function then another function can not read the channel position. How do I fix this.
The Code
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
var lastPosition:Number = 0; var CrossAudio = new Sound();
CrossAudio.load(new URLRequest("Cross.mp3"));
function playCross() {
PapaAudioChannel = CrossAudio.play();
}
bt_play.addEventListener(MouseEvent.CLICK, playA);
function playA(event:MouseEvent):void {
trace(PapaAudioChannel.position); // Does not work
/// It can't doesn't recognize the PapaAudioChannel because
/// it began playing in another function.
}
How can I get the second function to see the position of the audio. I need to start the audio with a play and pause button so the play has to start in a function.
Thanks
AFAIK the sound can start with a little delay, so tracing sound position just after a play() statement is not a good idea.
EDIT
By the way, where is PapaAudioChannel defined, if it is? (it should since this is AS3)
If you define a var in a function, it dies with that function, at the end of function!
So you may want to define PapaAudioChannel in the global scope:
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
var PapaAudioChannel:SoundChannel; // defines variable
var lastPosition:Number = 0; var CrossAudio = new Sound();
CrossAudio.load(new URLRequest("Cross.mp3"));
function playCross() {
PapaAudioChannel = CrossAudio.play();
}
bt_play.addEventListener(MouseEvent.CLICK, playA);
function playA(event:MouseEvent):void {
trace(PapaAudioChannel.position);
}

Embed vs Dynamically Loading sounds

Just wondering if anyone could help me with this. I'm new to actionscript, and am building an application that plays some sounds when buttons are clicked.
It's got 5 tabs, and will give the user the options to play about 10 sounds per tab.
I have initially been loading the sounds on runtime, so whenever the user clicked a button to play that sound, I would do something like:
var sound:Sound = new Sound(new URLRequest("assets/hello.mp3"));
sound.play();
I'm not sure, but I don't think this is very good, since I would be loading that sound over and over again if the user pressed the button too many times.
I then thought about embedding the sound in each of the views (I have one view per tab), so would embed the sounds whenever the view was loaded. I think this is a better options, but still am a bit unsure about how the embed works exactly.
[Embed('assets/hello.mp3')] private var hello_mp3:Class;
I suppose it simply embeds the mp3 files when the swf is compiled (making it bigger), but they would not be loaded anymore once the app starts, or once that view is initialized again.
My question is: Is this the right approach to take? Is there any better way I can accomplish this? Is embedding the right solution for my problem?
Thanks in advance
The first solution works fine, it won't make your SWf bigger and once the sound has been loaded once, it will be cached by Flash Player, so the sound is not loaded again and again.
This is a more efficient approach, sounds that are not played will not be loaded.
Bear in mind that when the button is pressed for the first time , you may experience a slight delay due to the sound being loaded.
In order to avoid this, you can load some sounds as external assets, meaning that after your SWF has loaded, you can call a function that will load some or all of the sounds, depending on the needs of the app. Your SWF will not be bloated and the button click will be more responsive.
Sounds like you need a SoundManager class, or something similar, that caches the sounds you need so that you can play them whenever you want and they only ever get loaded once. Classes like these, where you only ever need one instance of the class, are good candidates to be singletons. There are lots of ways to implement singleton design. In actionscript, my favorite way is like this:
//in SoundManager.as
public static var instance:SoundManager = new SoundManager();
Simple, and it works. Since the plan is that you'll only ever need one SoundManager, you can now get that one instance anywhere else in your code like this:
var soundManager:SoundManager = SoundManager.instance;
//or, more likely you can just use it in-line like this
SoundManager.instance.myMethod();
So now you've got your SoundManager. Let's set it up to keep a cache of sounds so that they only ever get loaded once:
// in SoundManager.as
private var _soundCache:Array = [];
public function getSound(soundName:String):Sound {
var testSound:Sound = _soundCache[soundName] as Sound;
if(!testSound) { //if the sound isn't loaded yet, testSound will be null
//the sound isn't there, so lets load it
var newSound:Sound = new Sound(new URLRequest(soundName));
_soundCache[soundName] = newSound;
return newSound;
}
//if we made it this far it means the sound was in the cache, so we return it
return testSound;
}
And presto, you only ever have to load the sound once! When you want to get a sound, it's as easy as:
var mySound:Sound = SoundManager.instance.getSound("mySound.mp3");
Let me know if you run into trouble with any of this code, but hopefully that's enough to get you on the right track.
In my opinion you should load all the sounds at the beggining and store references to each sound in a place you can access at anytime. A simple example would be storing each sound in an array. I've created a similar yet more robust application of this example:
sounds.xml
<?xml version="1.0" encoding="utf-8" ?>
<sounds>
<sound name="sound1" url="sound/sound1.mp3" />
<sound name="sound2" url="sound/sound2.mp3" />
<sound name="sound3" url="sound/sound3.mp3" />
<sound name="sound4" url="sound/sound4.mp3" />
<sound name="sound5" url="sound/sound5.mp3" />
</sounds>
Main.as(document class):
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.SoundChannel;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class Main extends Sprite
{
private var _soundLibrary:SoundLibrary;
public function Main():void
{
if (stage) init() else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
var urlLoader:URLLoader = new URLLoader(new URLRequest("xml/sounds.xml"));
urlLoader.addEventListener(Event.COMPLETE, onUrlLoaderComplete);
}// end function
private function onUrlLoaderComplete(e:Event):void
{
_soundLibrary = new SoundLibrary(XML(URLLoader(e.target).data));
_soundLibrary.addEventListener(Event.COMPLETE, onSoundLibraryComplete);
}// end function
private function onSoundLibraryComplete(e:Event):void
{
var soundChannel:SoundChannel = _soundLibrary.getSound("sound3").play();
}// end function
}// end class
}// end package
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.media.Sound;
import flash.net.URLRequest;
internal class SoundLibrary extends EventDispatcher
{
private var _xml:XML;
private var _length:int;
private var _counter:int;
private var _soundLibraryItems:Vector.<SoundLibraryItem>;
public function SoundLibrary(xml:XML):void
{
_xml = xml;
_length = _xml.children().length();
_soundLibraryItems = new Vector.<SoundLibraryItem>();
loadSounds();
}// end function
public function getSound(name:String):Sound
{
var sound:Sound;
for (var i:int = 0; i < _soundLibraryItems.length; i++)
{
if (_soundLibraryItems[i].name == name)
sound = _soundLibraryItems[i].sound;
}// end for
if (!sound) throw new ArgumentError("No sound object matches specified name");
return sound;
}// end function
private function loadSounds():void
{
for (var i:int = 0; i < _length; i++)
{
var sound:Sound = new Sound(new URLRequest(_xml.children()[i].#url));
sound.addEventListener(Event.COMPLETE, onSoundComplete);
}// end for
}// end function
private function onSoundComplete(e:Event):void
{
_soundLibraryItems.push(new SoundLibraryItem(_xml.children()[_counter].#name, Sound(e.target)));
if (++_counter == _length) dispatchEvent(new Event(Event.COMPLETE));
}// end function
}// end class
internal class SoundLibraryItem
{
private var _name:String;
private var _sound:Sound;
public function get name():String { return _name }
public function get sound():Sound { return _sound }
public function SoundLibraryItem(name:String, sound:Sound)
{
_name = name; _sound = sound;
}// end function
}// class
[UPDATE]
Summary
First the the sounds.xml is loaded and then on its completion it is parsed to the new instance of SoundLibrary.
SoundLibrary handles loading the sounds from the xml and then parses each loaded Sound object to a new instance of SoundLibraryItem.
SoundLibraryItem simply stores the name of the Sound object as well as the Sound object itself. When all the SoundLibraryItem objects are created the SoundLibrary object dispatches an Event object with an Event.COMPLETE type.
When the event is dispatched the event listener on the SoundLibrary object calls the onSoundLibraryComplete() event handler.
Finally the SoundLibrary object's getSound() method is used to get one of the previously loaded Sound objects via the name argument. Then with a SoundChannel object the Sound object's play() method is called.
In my opinion
Embed things is never an approuch
Pros:
You have anything in the same file = 1 load (
well, this is not really a Pro... )
Cons:
It takes longer to load the whole
application ( users don't like to
wait )
If you want to change the sound you must compile everything again
Scalability benefits
I'm sure people can help me with a lot more Cons ;)
Hope it helps

AS3 Stop external swf

Hi I'm loading an external swf into a MovieClip, and I want it to stop until I choose to play. Currently it plays upon loading immediately.
var mc:MovieClip;
var swfLoader:Loader = new Loader();
swfLoader.contentLoaderInfo.addEventListener (Event.COMPLETE, eventLoaded);
var request:URLRequest;
request = new URLRequest("external.swf");
swfLoader.load (request);
function eventLoaded(e:Event): void
{
mc = e.target.content as MovieClip;
// does not stop the clip
mc.Stop ();
}
So I tried adding a Event.ENTER_FRAME to the movieclip and stop it there, that will stop but it will play the first frame. Is there a way to get it to stay stopped when loaded until I choose Play?
It's actually very close to what Jochen Hilgers suggested. However, in this instance, the event you want is actually INIT instead of COMPLETE. INIT is fired when the content is not yet fully loaded but is ready for use (and will start playing on its own).
Attach the event with
loader.contentLoaderInfo.addEventListener(Event.INIT, handleReady );
And handle it with
public function handleReady( initEvent:Event ):void{
MovieClip(initEvent.currentTarget.content).stop();
}
You'll notice that you can cast the content property of currentTarget as a MovieClip and stop it even before it has been attached to the stage.
It is important to note that it is not safe to use the content property in a PROGRESS event (or any time prior to an INIT or COMPLETE event). You will get an error to the effect that the object is not ready.
I wrote this simple TestCase and it works fine... the loaded swf is quite simple, just a tween on the main timeline.
package {
import flash.display.Loader;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
public class Test extends Sprite
{
private var loader:Loader = new Loader;
public function Test()
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoaded );
loader.load( new URLRequest( 'testFile.swf' ) );
}
public function handleLoaded( event:Event ):void
{
addChild( loader.content );
var mc:MovieClip = loader.content as MovieClip ;
mc.stop();
}
}
}
I was looking for a similar problem/solution, but my problem was little diferent. I know this was not your issue, but looks fair to share my solution. When I tried to do
event.currentTarget.stop(); // AS1&AS2 -> BAD swf to import
with the content of a loader, my Flash IDE showed me this error:
"Property stop not found on flash.display.AVM1Movie and there is no default value."
This happened to me because the swf I imported was created using AS1, and not AS3 as the main movie ( so I decompiled the swf to a fla and recompiled using as3, it was an output from After Effects). Now I know AVM1 and AVM2 are classes that represent actionscript 1 and 2 files.