1

I'm really new to this, so, I'm really sorry if there is some obvious solution that I've somehow missed, but oh well...
I am in the process of making a game, and I was trying to make a character selection menu. Each character would have a unique version of the same song (with the same bps) so that when you were "hovering over" one of the choices, the character's song would play and the other ones would be muted (Not paused or stopped, so as not to lose the synchronisation between all the versions of the song)...
Right now, I was trying to program this for only one of the songs, and then, repeat the method for the other ones once I figured it out.
So...
What am I doing wrong?

Here's the code:

package {

import flash.display.MovieClip;
import flash.events.Event; 
import flash.media.SoundTransform;
import flash.media.Sound;
import flash.media.SoundChannel; 
import flash.media.SoundMixer; 

public class MainThing extends MovieClip {
    private var soulcounting: Number = 1;
    private var Muting: Boolean = false;
    private var snd:Sound = new Sound();

    public function MainThing() {
        snd.load(new URLRequest("SOUL1.mp3"));
        stage.addEventListener(Event.ENTER_FRAME, FrameHandler);
        }

    private function FrameHandler(event: Event): void {
        if (Object(root).currentFrame == 2) {
            var channel:SoundChannel = snd.play(0,1);
            channel.soundTransform = new SoundTransform(1,0);
            /*the soulcounting number is determined by a keyboard function...  
            I didn't deem it necessary to be included in here,  
            as I don't think it is part of the problem.*/
            if (soulcounting == 1) {
                Object(root).SOULS.gotoAndStop(1);
                Muting = false;
                }

            if (soulcounting == 2) {
                Object(root).SOULS.gotoAndStop(2);
                Muting = true;
                }

            if (soulcounting == 3) {
                Object(root).SOULS.gotoAndStop(3);
                Muting = true;
                }

            if (soulcounting == 4) {
                Object(root).SOULS.gotoAndStop(4);
                Muting = true;
                }

            if (soulcounting == 5) {
                Object(root).SOULS.gotoAndStop(5);
                Muting = true;
                }

            if (soulcounting == 6) {
                Object(root).SOULS.gotoAndStop(6);
                Muting = true;
                }

            if(Muting){
                setVolume(channel);
                }

            if(!Muting){
                setVolume(channel, 1);
                }
            } 

    private function setVolume (Soundchannel:SoundChannel, volume:Number=0) {
        var trans:SoundTransform = Soundchannel.soundTransform;
        trans.volume = volume;
        Soundchannel.soundTransform = trans;
        }
    }
}

(If I try to preview the swf, everything works fine until I get to the second frame, in which the Output starts repeating TypeError: Error #1009: Cannot access a property or method of a null object reference. at MainThing/FrameHandler())
TypeError #1009 seems to be a very common mistake around here, but what I have read hasn't really helped me with this problem... Sorry if there was already a solution in here, and I just didn't search enough...
Thanks in advance!

  • What is `Object(root)` supposed to mean? If it's some MovieClip on the stage why not give that MC an **instance name** then in code just target it with `if (myMCName.currentFrame == 2)`? – VC.One Apr 11 '17 at 02:38
  • Object(root) is the stage. – Mitsuki MK Kazen Apr 11 '17 at 13:31
  • Rule #1... Never make the stage use multiple frames (okay for cartoons but not for applications). Instead make a large (stage size) MovieClip as **container** and cut/paste all your older stage frames into it. If you called (instance name in ) the container as _contMC_ then your code can say `if (contMC.currentFrame == 2)` etc... – VC.One Apr 11 '17 at 13:49
  • I would still need to call that `Object(root).contMC` as I'm working from a .as file. Unless I add a link to the MovieClip, but that just makes it more complicated as I would have to use the `addChild` function... I didn't know about not using multiple frames in stage (shows how experienced I am), does it cause performance issues? – Mitsuki MK Kazen Apr 11 '17 at 14:10
  • No it avoids issues like access to on-stage items & variables. As a test draw a small box shape (rectangle, no outline just fill color) then right-click and choose convert to MClip. Now in **properties** (ctrl+F3) just put _box_ in instance name section. Press Enter to confirm changes then ctrl+s to save. Drag box MC to left side as starting point.. In your function `MainThing` put code `box.x += 100`.. test by ctrl+shift+enter... did the box move to a different position than was put on stage? – VC.One Apr 11 '17 at 14:28

2 Answers2

2

Please run your swf in debug mode and give us the line number that is causing the error. Don't know what cause the problem and not sure what you want to do but why you repeat yourself like that? It's even hard to read. Exactly the same thing you can do like that:

public class MainThing extends MovieClip {
    private var soulcounting: Number = 1;
    private var Muting: Boolean = false;
    private var snd:Sound = new Sound();

    public function MainThing() {
        snd.load(new URLRequest("SOUL1.mp3"));
        stage.addEventListener(Event.ENTER_FRAME, FrameHandler);
    }

    private function FrameHandler(event: Event): void {
        if (Object(root).currentFrame != 2) return;
        var channel:SoundChannel = snd.play(0,1);
        channel.soundTransform = new SoundTransform(1,0);
        /*the soulcounting number is determined by a keyboard function...  
        I didn't deem it necessary to be included in here,  
        as I don't think it is part of the problem.*/
        SOULS.gotoAndStop(soulcounting);
        Muting = soulcounting != 1;
        setVolume(channel, Number(!Muting));
    } 

    private function setVolume (sch:SoundChannel, volume:Number=0) {
        var trans:SoundTransform = sch.soundTransform;
        trans.volume = volume;
        sch.soundTransform = trans;
    }

}

And it would be even more efficient.

  • I'm not repeating myself? There are 6 options, and thus 6 frames the MovieClip has to go to if one of them is selected... I'm sorry that I wasn't clear about that. I put everything inside an if because I have other statements for the other stage frames... Maybe I should put them in separate functions, but... I'll check that later. (I'll try debug mode). – Mitsuki MK Kazen Apr 11 '17 at 13:34
  • Apparently, it is line 114 in my code, or: `channel.soundTransform = new SoundTransform(1,0);` – Mitsuki MK Kazen Apr 11 '17 at 13:40
  • The only thing you try to access here and thus the only thing that can be null is `channel`. And the channel you get from `snd.play();` and if you go to docs - [**play()**](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html#play()) you will read **This method returns null if you have no sound card or if you run out of available sound channels. The maximum number of sound channels available at once is 32.** We assume you have a sound card thus you must have call `play()` more than 32 times. Which now seem obvious as you have frame listener binded to stage. – Paweł Audionysos Apr 11 '17 at 15:15
  • How can I update the channel's volume without creating more sound channels then? – Mitsuki MK Kazen Apr 11 '17 at 15:24
  • Exacly the same as you do right now - `trans.volume`. Just don't start sounds at frame events... – Paweł Audionysos Apr 11 '17 at 15:27
  • I tried this: `public function MainThing() { snd.load(new URLRequest("SOUL1.mp3")); stage.addEventListener(Event.ENTER_FRAME, FrameHandler); Music(); } private function Music():void{ if(bool){ var channel:SoundChannel = snd.play(0,1); channel.soundTransform = new SoundTransform(1,0); if(Muting){ setVolume(channel); } if(!Muting){ setVolume(channel, 1); } } }` There's no error now, but the sound won't play... (I even tried a trace, it doesn't appear either) – Mitsuki MK Kazen Apr 11 '17 at 16:13
  • what and WHERE are you tracing? what is bool? – Paweł Audionysos Apr 11 '17 at 17:00
  • You just need to associate sound object with characters/souls/whatever and then when you switch the character you stop currently played sound and start associated sound at the same position you stopped previous sound. – Paweł Audionysos Apr 11 '17 at 17:06
  • I also tried this: `private function Music():void{ var channel:SoundChannel = snd.play(); channel.soundTransform = new SoundTransform(); if(soulcounting == 1){ setVolume(channel, 1); } else if(soulcounting > 1){ setVolume(channel, 0); } } private function setVolume (Schannel:SoundChannel, volume:Number=0) { var trans:SoundTransform = Schannel.soundTransform; trans.volume = volume; Schannel.soundTransform = trans; }` Didn't work as intended either. The thing is, how do I check at what point the previous song stopped? and tell it to start from that point? – Mitsuki MK Kazen Apr 11 '17 at 17:21
  • http://help.adobe.com/pl_PL/FlashPlatform/reference/actionscript/3/flash/media/SoundChannel.html#position is it really that hard to check the documentation for the classes you are working with? – Paweł Audionysos Apr 11 '17 at 17:31
  • ... sorry. Thanks for the link. I'll try that. – Mitsuki MK Kazen Apr 11 '17 at 17:47
1

Thanks to Pawel Audionysos and VC.One for helping me with my problem. I solved it! Instead of using the SoundTransform.volume function, I used the SoundChannel.position and a variable to "save" the position at which the previous song stopped, so that the next one would start from that point: Here's the code if anyone's interested:

package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.media.SoundTransform;
import flash.media.Sound;
import flash.media.SoundChannel; 
import flash.media.SoundMixer; 
import flash.net.URLLoader;
import flash.net.URLRequest;

public class MainThing extends MovieClip {
    private var bool:Boolean = false;
    private var snd1:Sound = new Sound();
    private var snd2:Sound = new Sound();
    private var snd3:Sound = new Sound();
    private var snd4:Sound = new Sound();
    private var snd5:Sound = new Sound();
    private var snd6:Sound = new Sound();
    private var channel1:SoundChannel = new SoundChannel();
    private var channel2:SoundChannel = new SoundChannel();
    private var channel3:SoundChannel = new SoundChannel();
    private var channel4:SoundChannel = new SoundChannel();
    private var channel5:SoundChannel = new SoundChannel();
    private var channel6:SoundChannel = new SoundChannel();
    private var songtime:Number = 0;
    private var NoSongHasBeenPlayedYet:Boolean = true;
    private var comesfromnothing:Boolean = true;
    private var comesfrom1:Boolean = false;
    private var comesfrom2:Boolean = false;
    private var comesfrom3:Boolean = false;
    private var comesfrom4:Boolean = false;
    private var comesfrom5:Boolean = false;
    private var comesfrom6:Boolean = false;

    public function MainThing() {
        snd1.load(new URLRequest("SOUL1.mp3"));
        snd2.load(new URLRequest("SOUL2.mp3"));
        snd3.load(new URLRequest("SOUL3.mp3"));
        snd4.load(new URLRequest("SOUL4.mp3"));
        snd5.load(new URLRequest("SOUL5.mp3"));
        snd6.load(new URLRequest("SOUL6.mp3"));
        stage.addEventListener(KeyboardEvent.KEY_DOWN, MenuButtons);

        stage.addEventListener(Event.ENTER_FRAME, FrameHandler);
    }
    private function FrameHandler(event: Event): void {
        if (Object(root).currentFrame == 2) {
            //I ommitted the part of the changes to the MovieClip's frames
            //the music function is called here... It didn't work anywhere else.
            Music();
            }
        } 
    private function Music():void{
        if(NoSongHasBeenPlayedYet){
            //this part happens either when the frame has just changed, or when the "songtime" variable has surpassed the audio length.
            comesfromnothing = true;
            channel1 = snd1.play(0,100);
            channel2 = snd2.play(0,100);
            channel3 = snd3.play(0,100);
            channel4 = snd4.play(0,100);
            channel5 = snd5.play(0,100);
            channel6 = snd6.play(0,100);
            NoSongHasBeenPlayedYet = false;
            bool = false;

            }
        if(!bool && songtime < 15150){
            switch(soulcounting){
                //In the original, there are 6 cases, for each song. I didn't put all of them in here because they are mostly the same, just with different numbers.
                case 1:
                    if(comesfromnothing){
                        trace(songtime);
                        songtime = channel2.position;
                        channel1.stop();
                        channel2.stop();
                        channel3.stop();
                        channel4.stop();
                        channel5.stop();
                        channel6.stop();
                        channel1 = snd1.play(songtime,100);
                        songtime = channel1.position;   
                        //the bool variable is there so that it doesn't create a new SoundChannel at every frame    
                        bool = true;
                        comesfromnothing = false;
                        comesfrom1 = true;

                    }
                    if(comesfrom2){
                        trace(songtime);
                        songtime = channel2.position;
                        channel1.stop();
                        channel2.stop();
                        channel1 = snd1.play(songtime,100);
                        songtime = channel1.position;       
                        bool = true;
                        comesfrom1 = true;
                        comesfrom2 = false;

                    }
                    if(comesfrom3){
                        trace(songtime);
                        songtime = channel3.position;
                        channel1.stop();
                        channel3.stop();
                        channel1 = snd1.play(songtime,100);
                        songtime = channel1.position;       
                        bool = true;
                        comesfrom1 = true;
                        comesfrom3 = false;
                    }
                    if(comesfrom4){
                        trace(songtime);
                        songtime = channel4.position;
                        channel1.stop();
                        channel4.stop();
                        channel1 = snd1.play(songtime,100);
                        songtime = channel1.position;       
                        bool = true;
                        comesfrom1 = true;
                        comesfrom3 = false;
                    }
                break;
            if(channel1.position  > 15100){
                trace(songtime);
                songtime = 0;
                channel1.stop();
                channel2.stop();
                channel3.stop();
                channel4.stop();
                channel5.stop();
                channel6.stop();
                NoSongHasBeenPlayedYet = true;

            }
        }
        public function MenuButtons(event: KeyboardEvent): void {
            if (Object(root).currentFrame == 2) {

            //this is the part of the controls, the bool is set to false so each time you press a botton, the audio can change.
            if (Keyboard.LEFT == event.keyCode && soulcounting != 1 && soulcounting != 4 && !Keyboardpressed) {
                soulcounting -= 1;
                bool = false;
                Keyboardpressed = true;} 
            else if (Keyboard.LEFT == event.keyCode && (soulcounting == 1 || soulcounting == 4) && !Keyboardpressed) {
                soulcounting += 2;
                bool = false;
                Keyboardpressed = true;} 
            else if (Keyboard.RIGHT == event.keyCode && soulcounting != 3 && soulcounting != 6 && !Keyboardpressed) {
                soulcounting += 1;
                bool = false;
                Keyboardpressed = true;} 
            else if (Keyboard.RIGHT == event.keyCode && (soulcounting == 3 || soulcounting == 6) && !Keyboardpressed) {
                soulcounting -= 2;
                bool = false;
                Keyboardpressed = true;} 
            else if ((Keyboard.UP == event.keyCode || Keyboard.DOWN == event.keyCode) && soulcounting > 3 && !Keyboardpressed) {
                soulcounting -= 3;
                bool = false;
                Keyboardpressed = true;} 
            else if ((Keyboard.DOWN == event.keyCode || Keyboard.UP == event.keyCode) && soulcounting < 4 && !Keyboardpressed) {
                soulcounting += 3;
                bool = false;
                Keyboardpressed = true;
                }
            }
        }
    }
  • Nice you got it working but the way you programmed it is not good. It's not good that you start all sounds at once and then pause different individual sounds on each possible case. Imagine that instead of six characters you have now 50 of them. 1. it's not going to work because of 32 polyphony limit and 2. you would need to have 50 `if` blocks and in each 49 `channelX.stop()` call. Also what is switch construct for if you use one case? And what is bool? It's hart to figure that out for other peoples. – Paweł Audionysos Apr 13 '17 at 17:40
  • Check [**this answer**](http://stackoverflow.com/a/42831679/2511919) to maybe get some idea how you can design it. – Paweł Audionysos Apr 13 '17 at 17:41
  • I see your point, I'll try to look how can I make it more efficient. There are 6 cases in the original program, all of them are the same as the case shown here, with the exception of the number of the song changing: channel1 in case 1, channel2 in case 2, etc. The "bool" is just a variable that I made to stop the program from creating a new channel every single frame... i guess it could be called "SongChannelHasBeenCreated"... But "bool" is shorter, and I hadn't used that name yet... Sorry about the confusion... It really shows I'm a newbie doesn't it? – Mitsuki MK Kazen Apr 13 '17 at 23:53
  • Noting wrong with been newbie. I remember one of my fist project when I programmed 2d projecttion of rubic cube. I thougth add 3th level nested loop would be to complicated and "I just have few cases"... Ended up with 4K lines of code where today I would probably do this within 200 lines. :) Think what is repeating and what could you reuse. For example. you don't need to create `new SoundChannel();` at declaration, it's pointless. Instead just say `private var channel1:SoundChannel;` at the start the channel1 will be `null` so you can check that fact to determine if you started sound already. – Paweł Audionysos Apr 14 '17 at 22:35
  • Also what's the reason you call `music()` on frame listener? As I understand you just want to change music on mouse action. so why don't you put it there? – Paweł Audionysos Apr 14 '17 at 22:38
  • I'll see what I can do in the efficiency front, for now, I just want to get it working properly. (I think I wrote it that way because I thought it was the cause of the TypeEvent null error. Of course, it wasn't, so I'll change it.) I actually wanted to change the song with the keyboard. I tried putting the function inside the KeyboardEvent function, but it wouldn't work as intended. (A new channel would be created everytime I pressed any button, for example.) – Mitsuki MK Kazen Apr 15 '17 at 18:59
  • It's hard to figure out what is exactly happening in code written like that but generally if you want to resume a sound you have to create new instance of `SoundChannel` because you have to call `play()`. Channels are not reusable in that sense. The think you try to do is very easy but I can't write it for you. Try to figure out the example I linked. Once you understand it, changing it for your need should be easy. – Paweł Audionysos Apr 15 '17 at 20:25
  • With the current program it does work correctly though. – Mitsuki MK Kazen Apr 16 '17 at 02:16