0

I'm trying to run code to show 30 different images each that have their own 30- second music clip using ActionScript 3.0 in Adobe Animate CC. I'm having a problem trying to loop through the set (load the next image after the song for the image shown plays). I can get the first image and 30-second song to load, but it won't continue looping through to the set. I have a numeric var I'm using to point to the art file and song on my system. I'm successfully checking to ensure the sound file is done playing before trying to move to the next image and sound file, but when I attempt to enclose the code in a loop, it coughs up the error:

Functions called in incorrect sequence, or earlier call was unsuccessful. at flash.media::Sound/_load() at flash.media::Sound/load()

Any help is appreciated. Thanks.

import flash.display.MovieClip;
import flash.display.Loader;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import flash.events.Event;

var songart:Number = 1;

// Create directory variables
var imgs:String = "F:/Synthsation/Web and Flash Design/Adobe     Animate/Duke_Nukem_TLB_Album_Art/Album_Art/";
var music:String = "F:/Synthsation/Web and Flash Design/Adobe Animate/Duke_Nukem_TLB_Album_Art/Song_Tracks/";

// Create Loader and movie clip variables
var imageLoader:Loader = new Loader();
var songclip:Sound = new Sound();

// Begin processing
// Loop through the album art and load the appropriate music clip
// set songart to begin at 1 and loop until completed

//  for (songart = 1; songart < 31; songart++) {
while (songart < 31) {

    // Convert the song number into a string 
    var songString:String = String(songart);

    // ------ QUEUE UP THE MUSIC ----------
    var mp3request:URLRequest = new URLRequest(music + songString + ".mp3");
    songclip.load(mp3request);

    // Create sound channel variable and tie it to sound object
    var channel:SoundChannel = songclip.play();
        songclip.play(); 

    // ------ LOAD THE PICTURE -----
    var picrequest:URLRequest = new URLRequest(imgs + songString + ".jpg");
    imageLoader.load(picrequest);

    // Add picture and position at top left of stage
    addChild (imageLoader);
    imageLoader.x = 0;
    imageLoader.y = 0;

     channel.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete); 
    // Determine if the song has finished playing. If so loop to next iteration
    function onPlaybackComplete(event:Event): void
    {
    //  trace("DONE PLAYING!");
        trace(songart);
    //removeChild (imageLoader);    < --- necessary for next image?
        }
    }   

1 Answers1

0

It's a common beginner's (no offense, we all have been through it) misconception about the way Flash Player works. Plainly, Flash Player executes things on asynchronous frame-by-frame basis, each frame includes two major phases: 1) script and events execution then 2) stage rendering and external (like loading and networking) operations. Then, unless you are into programming animations, you don't even need to think in frame-by-frame terms, just assume it is an event-driven asynchronous environment. There's no main thread, only event handlers (the entry point is event handler for application start event, frame scripts are event handlers for playhead entering this frame) which are expected to execute in a single given moment, not any longer.

So, if you loop it like you do, your whole script executes at once, moreover, on the same Sound instance. Yes, you are trying to load 30 audios to the same Sound instance simultaneously. Of course, it fails like that. The same goes for Loader instance.

What you need is a component approach. First, you need a component capable of performing a granular operation: load image and sound by their index, play audio, report the audio is done playing, dispose of everything:

package
{
    import flash.display.Sprite;
    import flash.display.Loader;

    import flash.media.Sound;
    import flash.media.SoundChannel;

    import flash.events.Event;
    import flash.net.URLRequest;

    public class SoundImage extends Sprite
    {
        private static const IMG:String = "F:/Synthsation/Web and Flash Design/Adobe Animate/Duke_Nukem_TLB_Album_Art/Album_Art/";
        private static const SND:String = "F:/Synthsation/Web and Flash Design/Adobe Animate/Duke_Nukem_TLB_Album_Art/Song_Tracks/";

        private var Image:Loader;

        private var Audio:Sound;
        private var Channel:SoundChannel;

        public function start(index:int):void
        {
            // Load image by index.
            // There are no () brackets, it is not an error.
            // If there are no arguments you can omit them with "new" operator.
            Image = new Loader;
            Image.load(new URLRequest(IMG + index + ".jpg"));

            addChild(Image);

            // Load audio by index.
            Audio = new Sound;
            Audio.load(new URLRequest(SND + index + ".mp3"));

            // Play audio and listen for it to complete.
            Channel = Audio.play();
            Channel.addEventListener(Event.SOUND_COMPLETE, onDone); 
        }

        private function onDone(e:Event):void
        {
            // Remove the subscription.
            Channel.removeEventListener(Event.SOUND_COMPLETE, onDone);

            // Let all the subscribers know the audio is done.
            dispatchEvent(new Event(Event.COMPLETE));
        }

        public function dispose():void
        {
            // Always do the clean-up to avoid memory leaks
            // and unhandled things lying dormant somewhere.

            // Sanity check to avoid errors if you call that twice.
            // Or if you call it without actually starting it.
            if (!Image) return;

            // Yes, remove the subscription, because you might want
            // to stop the component before audio is done playing.
            Channel.removeEventListener(Event.SOUND_COMPLETE, onDone);

            Channel.stop();
            Channel = null;

            Audio.close();
            Audio = null;

            removeChild(Image);

            Image.unloadAndStop(true);
            Image = null;
        }
    }
}

Then, to play a single entry, you need to

var SI:SoundImage = new SoundImage;

SI.start(10);
addChild(SI);

If you want to play them one by one, the idea is to create one, then wait (in asynchronous event-driven manner, of course) until it is done, then proceed to the next one:

var SI:SoundImage;
var songIndex:int;

playNext();

function playNext():void
{
    songIndex++;

    SI = new SoundImage;

    // Listen for SI to complete playing audio.
    SI.addEventListener(Event.COMPLETE, onSong);
    SI.start(songIndex);

    addChild(SI);
}

function onSong(e:Event):void
{
    removeChild(SI);

    // Unsubscribe from event to release the instance.
    SI.removeEventListener(Event.COMPLETE, onSong);

    // Clean up things.
    SI.dispose();
    SI = null;

    // Proceed to the next entry.
    playNext();
}

Ideally you need to empower the component with error handling in case Loader or Sound fails loading its content, as well as some logic above to handle such a case.

P.S. I didn't check the scripts, my goal was to explain your mistake and the correct approach.

Organis
  • 7,243
  • 2
  • 12
  • 14