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);
}
Related
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();
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
I have called a setInterval on TouchEvent.TOUCH_END and I want to clear it when ever screen is touched.
Here is my code:
import fl.motion.MotionEvent;
import flash.display.MovieClip;
import flash.utils.*;
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
stage.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
function onTouchBegin(evt:TouchEvent)
{
clearInterval(MovieClip(root).myInterval);
}
function onTouchEnd(evt:TouchEvent)
{
MovieClip(root).myInterval = setInterval(showTimer,1000);
}
function showTimer()
{
trace("interval working");
}
Your provided code doesn't really explain everything so there will be a fair bit of speculation in this answer:
Possiblity No. 1:
Your way of casting root to MovieClip is not working. Try changing to following:
function onTouchBegin(evt:TouchEvent)
{
var intervalRef:int = (root as MovieClip).myInterval;
clearInterval(intervalRef);
}
Possiblity No. 2:
It seems that your code is written in a frame. And although if you define something in a previous frame it should work, but if you have the code on a different (lower) layer on the same keyframe it could define the variable later and your MovieClip(root).myInterval variable is undefined or null is the value is not defined. So check if your variable even exists:
function onTouchBegin(evt:TouchEvent)
{
trace(MovieClip(root) == null); // see if the root is defined
trace(MovieClip(root).myInterval); // see if the myInterval is defined
}
Possiblity No. 3:
You're cycling through frames. When I had 2 frames: one blank and one with your code, the code didn't work properly. Tested in Flash CC.
Possibility No. 4:
You have a run-time error somewhere else and your whole frame's code is not being executed. Do you use the debug version of Flash Player?
Possible solution for less headache:
Use a Timer. It's easy to control, easy to manage and dispose of. I've edited your code to work with Timer and tested. Feel free to use it for your project.
import fl.motion.MotionEvent;
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
var timer:Timer = (timer == null) ? new Timer(1000) : timer;
timer.addEventListener(TimerEvent.TIMER, onTick);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onTouchBegin); // you can change back to TouchEvent, but since you're using TOUCH_POINT it's pointless so I'd just use MouseEvent
stage.addEventListener(MouseEvent.MOUSE_UP, onTouchEnd);
function onTouchBegin(evt:Event)
{
timer.stop();
}
function onTouchEnd(evt:Event)
{
timer.start();
}
function showTimer()
{
trace("interval working");
}
function onTick(e:TimerEvent):void
{
showTimer();
}
I do want to note that your code works if I just copy-paste it in a single frame Flash file. But still, it's encouraged by Adobe to use Timer instead.
I realized that Event.SOUND_COMPLETE event isn't fired when starting playback from the sound.length position (it can be used when implementing scrubber: user can seek to 100% and start playing).
Is this bug? What is the common practice to workaround it?
Sample code reproducing this behavior:
import flash.media.Sound;
import flash.media.SoundChannel;
private var sound:Sound;
private var channel:SoundChannel;
public function test():void {
sound = new Sound();
sound.addEventListener(Event.COMPLETE, testLoadingCompleteHandler);
sound.load(new URLRequest("/song.mp3"));
}
private function testLoadingCompleteHandler(event:Event):void {
// SOUND_COMPLETE doesn't fire when we start playing from sound.length position
trace("# testSoundLoadingCompleteHandler()");
channel = sound.play(sound.length);
channel.addEventListener(Event.SOUND_COMPLETE, testPlaybackCompleteHandler);
}
private function testPlaybackCompleteHandler(event:Event):void {
// This message won't be printed because SOUND_COMPLETE event isn't fired
trace("# testPlaybackCompleteHandler()");
}
Maybe sound.play(sound.length) does not starts playback, or even returns error. I suppose it may be because sound.length is bigger than allowed value. Like in arrays you cannot use array[array.length], because it will be out of range.
I recomend triyng array.length-1
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.