开发者

Error while setting volume on SoundEffectInstance

I am seeing an error be reported quite frequently in my application. I know where it is happening, I just don't know why. Th开发者_StackOverflowe app plays sound effects (not background music), I am using Xna.Audio and I have a timer to call FrameworkDispatcher.Update() in regular intervals. I am using SoundEffect.CreateInstance so I can I have the sound effect loop. It looks like an error happens when changing the volume. I don't know WHY though. Are there known instances when this can fail?

Function ::: Offset


xxx_RaiseException ::: 32

WatsonUnhandledManagedException ::: 300

Dbg_NotifyManagedException ::: 136

FirstPassException ::: 1044

TransitionStub ::: 0

Microsoft.Xna.Framework.Helpers.ThrowExceptionFromErrorCode ::: 76

Microsoft.Xna.Framework.Audio.SoundEffectInstance.set_Volume ::: 232

Microsoft.Xna.Framework.Audio.SoundEffectInstance..ctor ::: 232

Microsoft.Xna.Framework.Audio.SoundEffect.CreateInstance ::: 192

AgiliTrain.PhoneyTools.Media.SoundEffectPlayer..ctor ::: 96

WildSafari.ClassicModeGame.animalVisibleTimer_Tick ::: 344

...snip....

For those asking, here is more code. The SoundEffectPlayer takes a SoundEffect, creates an instance, and kicks off the XNA FrameworkDispatcher (via the GameTimer). This code is taken from PhoneyTools, so go check out the codeplex project if you need more context. When I want to play an effect, I just new up a SoundEffectPlayer, and pass in what I want.

public class SoundEffectPlayer
{
    GameTimer _xnaTimer = new GameTimer();
    SoundEffectInstance _effect = null;
    public float _duration;

    public SoundEffectPlayer(SoundEffect effect, bool loop)
    {
        _effect = effect.CreateInstance();
        _effect.IsLooped = loop;
        _duration = (float)effect.Duration.TotalSeconds;
    }

    public void Play(float volume)
    {
        _xnaTimer.Start();
        _effect.Play();
        _effect.Volume = volume;
    }

    public void Stop()
    {

        _effect.Stop(true);
        _xnaTimer.Stop();
    }
}

public class GameTimer
{
    DispatcherTimer _timer = new DispatcherTimer()
    {
        Interval = TimeSpan.FromMilliseconds(50),
    };

    public GameTimer()
    {
        _timer.Tick += new EventHandler(_timer_Tick);
    }

    void _timer_Tick(object sender, EventArgs e)
    {
        FrameworkDispatcher.Update();
    }

    public void Start()
    {
        if (!_timer.IsEnabled) _timer.Start();
    }

    public void Stop()
    {
        if (_timer.IsEnabled) _timer.Stop();
    }
}


Trying to help out, although i don't have a valid fix...

The setter property for SoundEffectInstance is not as simple as one might think:

  set
        {
            lock (this.voiceHandleLock)
            {
                if (this.IsDisposed)
                {
                    throw new ObjectDisposedException(base.GetType().Name, FrameworkResources.ObjectDisposedException);
                }
                if ((value < 0f) || (value > 1f))
                {
                    throw new ArgumentOutOfRangeException("value");
                }
                Helpers.ThrowExceptionFromErrorCode(SoundEffectUnsafeNativeMethods.SetVolume(this.voiceHandle, value));
                this.currentVolume = value;
            }
        }

Basically the part we're after comes in the line before last.

If the call from SoundEffectUnsafeNativeMethods.SetVolume returns any code smaller than 0, the exception you're getting will be triggered.

Here's what's happening inside the unsafe method:

public static unsafe int modopt(IsLong) SetVolume(uint soundEffectInstanceHandle, float volume)
{
    lock (SoundEffectSubsystemSyncObject)
    {
        if ((soundEffectInstanceHandle != 0) && (soundEffectInstanceHandle != uint.MaxValue))
        {
            CHandleTable* tablePtr = ?g_pTable@CHandleTable@@0PAV1@A;
            KernelSoundEffectInstance* instancePtr = CHandleTable.LookUp<class Microsoft::Xna::Framework::Audio::KernelSoundEffectInstance>((CHandleTable modopt(IsConst)* modopt(IsConst) modopt(IsConst)) ?g_pTable@CHandleTable@@0PAV1@A, soundEffectInstanceHandle, 0);
            if (instancePtr == null)
            {
                return -2147024809;
            }
            return **(((int*) instancePtr))[0x34](instancePtr, volume);
        }
        return -2147024809;
    }
}

You can try to surround the creation of _effect.CreateInstance with try...catch and rethrow a more comprehensive and helpful exception that will contain for example, information about the SoundEffect object which you're trying to create (basically you can note down all of its field's values for inspection. (not sure that what you get from MS is only a stack trace or the actual exception messaage).

It seems that on WP7, the call to set the volume goes to some OS pointer table that does some magic behind the scenes and looks for the sound effect handle in this table.

If for some reason it is not found or something similar (don't know why, maybe we can take it with XNA developers), it will throw this exception you're getting.

I know this is not a solution but maybe this is a step in the right direction.


Generally, the exception should not be thrown for the situation you are describing, as long as you built the XNA compatibility layer as it should be (details here).

However, I noticed that you are using PhoneyTools.Media.SoundEffectPlayer - what for? It might be the cause of the problem in your situation.

Also, you seem to have ambiguous references - effect and _effect - which one are you working with when playing content?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜