internal AudioCategory(AudioEngine audioengine, string name, BinaryReader reader)
        {
            Debug.Assert(audioengine != null);
            Debug.Assert(!string.IsNullOrEmpty(name));

            _sounds = new List <XactSound>();
            _name   = name;
            _engine = audioengine;

            maxInstances  = reader.ReadByte();
            instanceLimit = maxInstances != 0xff;

            fadeIn  = (reader.ReadUInt16() / 1000f);
            fadeOut = (reader.ReadUInt16() / 1000f);

            byte instanceFlags = reader.ReadByte();

            fadeType         = (CrossfadeType)(instanceFlags & 0x7);
            InstanceBehavior = (MaxInstanceBehavior)(instanceFlags >> 3);

            reader.ReadUInt16();  //unkn

            var volume = XactHelpers.ParseVolumeFromDecibels(reader.ReadByte());

            _volume = new float[1] {
                volume
            };

            byte visibilityFlags = reader.ReadByte();

            isBackgroundMusic = (visibilityFlags & 0x1) != 0;
            isPublic          = (visibilityFlags & 0x2) != 0;
        }
Example #2
0
        public XactSound(SoundBank soundBank, BinaryReader soundReader, uint soundOffset)
        {
            _soundBank = soundBank;

            var oldPosition = soundReader.BaseStream.Position;

            soundReader.BaseStream.Seek(soundOffset, SeekOrigin.Begin);

            byte flags = soundReader.ReadByte();

            _complexSound = (flags & 1) != 0;

            _categoryID = soundReader.ReadUInt16();
            var volume = XactHelpers.ParseVolumeFromDecibels(soundReader.ReadByte());
            var pitch  = soundReader.ReadInt16() / 1000.0f;

            soundReader.ReadByte();    //unkn
            soundReader.ReadUInt16();  // entryLength

            uint numClips = 0;

            if (_complexSound)
            {
                numClips = (uint)soundReader.ReadByte();
            }
            else
            {
                _trackIndex    = soundReader.ReadUInt16();
                _waveBankIndex = soundReader.ReadByte();
            }

            if ((flags & 0x1E) != 0)
            {
                uint extraDataLen = soundReader.ReadUInt16();
                //TODO: Parse RPC+DSP stuff

                // extraDataLen - 2, we need to account for extraDataLen itself!
                soundReader.BaseStream.Seek(extraDataLen - 2, SeekOrigin.Current);
            }

            if (_complexSound)
            {
                _soundClips = new XactClip[numClips];
                for (int i = 0; i < numClips; i++)
                {
                    soundReader.ReadByte();                      //unkn
                    uint clipOffset = soundReader.ReadUInt32();
                    soundReader.ReadUInt32();                    //unkn

                    _soundClips[i] = new XactClip(soundBank, soundReader, clipOffset);
                }
            }

            var category = soundBank.AudioEngine.Categories[_categoryID];

            category.AddSound(this);

            soundReader.BaseStream.Seek(oldPosition, SeekOrigin.Begin);
        }
Example #3
0
        public XactSound(SoundBank soundBank, BinaryReader soundReader)
        {
            _soundBank = soundBank;

            var flags = soundReader.ReadByte();

            _complexSound = (flags & 0x1) != 0;
            var hasRPCs    = (flags & 0x0E) != 0;
            var hasEffects = (flags & 0x10) != 0;

            _categoryID = soundReader.ReadUInt16();
            _volume     = XactHelpers.ParseVolumeFromDecibels(soundReader.ReadByte());
            _pitch      = soundReader.ReadInt16() / 1000.0f;
            var priority = soundReader.ReadByte();

            soundReader.ReadUInt16(); // filter stuff?

            uint numClips = 0;

            if (_complexSound)
            {
                numClips = soundReader.ReadByte();
            }
            else
            {
                _trackIndex    = soundReader.ReadUInt16();
                _waveBankIndex = soundReader.ReadByte();
            }

            if (hasRPCs)
            {
                var current    = soundReader.BaseStream.Position;
                var dataLength = soundReader.ReadUInt16();
                soundReader.BaseStream.Seek(current + dataLength, SeekOrigin.Begin);
            }

            if (hasEffects)
            {
                var current    = soundReader.BaseStream.Position;
                var dataLength = soundReader.ReadUInt16();
                soundReader.BaseStream.Seek(current + dataLength, SeekOrigin.Begin);
            }

            if (_complexSound)
            {
                _soundClips = new XactClip[numClips];
                for (int i = 0; i < numClips; i++)
                {
                    _soundClips[i] = new XactClip(soundBank, soundReader);
                }
            }

            var category = soundBank.AudioEngine.Categories[_categoryID];

            category.AddSound(this);
        }
Example #4
0
        internal static void PlatformSetReverbSettings(ReverbSettings reverbSettings)
        {
#if SUPPORTS_EFX
            if (!OpenALSoundController.Efx.IsInitialized)
            {
                return;
            }

            if (ReverbEffect != 0)
            {
                return;
            }

            var efx = OpenALSoundController.Efx;
            efx.GenAuxiliaryEffectSlots(1, out ReverbSlot);
            efx.GenEffect(out ReverbEffect);
            efx.Effect(ReverbEffect, EfxEffecti.EffectType, (int)EfxEffectType.Reverb);
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbReflectionsDelay, reverbSettings.ReflectionsDelayMs / 1000.0f);
            efx.Effect(ReverbEffect, EfxEffectf.LateReverbDelay, reverbSettings.ReverbDelayMs / 1000.0f);
            // map these from range 0-15 to 0-1
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbDiffusion, reverbSettings.EarlyDiffusion / 15f);
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbDiffusion, reverbSettings.LateDiffusion / 15f);
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbGainLF, Math.Min(XactHelpers.ParseVolumeFromDecibels(reverbSettings.LowEqGain - 8f), 1.0f));
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbLFReference, (reverbSettings.LowEqCutoff * 50f) + 50f);
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbGainHF, XactHelpers.ParseVolumeFromDecibels(reverbSettings.HighEqGain - 8f));
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbHFReference, (reverbSettings.HighEqCutoff * 500f) + 1000f);
            // According to Xamarin docs EaxReverbReflectionsGain Unit: Linear gain Range [0.0f .. 3.16f] Default: 0.05f
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbReflectionsGain, Math.Min(XactHelpers.ParseVolumeFromDecibels(reverbSettings.ReflectionsGainDb), 3.16f));
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbGain, Math.Min(XactHelpers.ParseVolumeFromDecibels(reverbSettings.ReverbGainDb), 1.0f));
            // map these from 0-100 down to 0-1
            efx.Effect(ReverbEffect, EfxEffectf.EaxReverbDensity, reverbSettings.DensityPct / 100f);
            efx.AuxiliaryEffectSlot(ReverbSlot, EfxEffectSlotf.EffectSlotGain, reverbSettings.WetDryMixPct / 200f);

            // Dont know what to do with these EFX has no mapping for them. Just ignore for now
            // we can enable them as we go.
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionLeft, reverbSettings.PositionLeft);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionRight, reverbSettings.PositionRight);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionLeftMatrix, reverbSettings.PositionLeftMatrix);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.PositionRightMatrix, reverbSettings.PositionRightMatrix);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RearDelayMs);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomFilterFrequencyHz);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomFilterMainDb);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomFilterHighFrequencyDb);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.DecayTimeSec);
            //efx.SetEffectParam (ReverbEffect, EfxEffectf.LowFrequencyReference, reverbSettings.RoomSizeFeet);

            efx.BindEffectToAuxiliarySlot(ReverbSlot, ReverbEffect);
#endif
        }
Example #5
0
        private float UpdateRpcCurves()
        {
            var volume = 1.0f;

            // Evaluate the runtime parameter controls.
            var rpcCurves = _curSound.RpcCurves;

            if (rpcCurves.Length > 0)
            {
                var   pitch           = 0.0f;
                var   reverbMix       = 1.0f;
                float?filterFrequency = null;
                float?filterQFactor   = null;

                for (var i = 0; i < rpcCurves.Length; i++)
                {
                    var rpcCurve = _engine.RpcCurves[rpcCurves[i]];

                    // Some curves are driven by global variables and others by cue instance variables.
                    float value;
                    if (rpcCurve.IsGlobal)
                    {
                        value = rpcCurve.Evaluate(_engine.GetGlobalVariable(rpcCurve.Variable));
                    }
                    else
                    {
                        value = rpcCurve.Evaluate(_variables[rpcCurve.Variable].Value);
                    }

                    // Process the final curve value based on the parameter type it is.
                    switch (rpcCurve.Parameter)
                    {
                    case RpcParameter.Volume:
                        volume *= XactHelpers.ParseVolumeFromDecibels(value / 100.0f);
                        break;

                    case RpcParameter.Pitch:
                        pitch += value / 1000.0f;
                        break;

                    case RpcParameter.ReverbSend:
                        reverbMix *= XactHelpers.ParseVolumeFromDecibels(value / 100.0f);
                        break;

                    case RpcParameter.FilterFrequency:
                        filterFrequency = value;
                        break;

                    case RpcParameter.FilterQFactor:
                        filterQFactor = value;
                        break;

                    default:
                        throw new ArgumentOutOfRangeException("rpcCurve.Parameter");
                    }
                }

                pitch = MathHelper.Clamp(pitch, -1.0f, 1.0f);
                if (volume < 0.0f)
                {
                    volume = 0.0f;
                }

                _curSound.UpdateState(_engine, volume, pitch, reverbMix, filterFrequency, filterQFactor);
            }

            return(volume);
        }
Example #6
0
        public XactSound(AudioEngine engine, SoundBank soundBank, BinaryReader soundReader)
        {
            _soundBank = soundBank;

            var flags = soundReader.ReadByte();

            _complexSound = (flags & 0x1) != 0;
            var hasRPCs = (flags & 0x0E) != 0;
            var hasDSPs = (flags & 0x10) != 0;

            _categoryID = soundReader.ReadUInt16();
            _volume     = XactHelpers.ParseVolumeFromDecibels(soundReader.ReadByte());
            _pitch      = soundReader.ReadInt16() / 1000.0f;
            soundReader.ReadByte();   //priority
            soundReader.ReadUInt16(); // filter stuff?

            var numClips = 0;

            if (_complexSound)
            {
                numClips = soundReader.ReadByte();
            }
            else
            {
                _trackIndex    = soundReader.ReadUInt16();
                _waveBankIndex = soundReader.ReadByte();
            }

            if (!hasRPCs)
            {
                RpcCurves = new int[0];
            }
            else
            {
                var current = soundReader.BaseStream.Position;

                // This doesn't seem to be used... might have been there
                // to allow for some future file format expansion.
                var dataLength = soundReader.ReadUInt16();

                var numPresets = soundReader.ReadByte();
                RpcCurves = new int[numPresets];
                for (var i = 0; i < numPresets; i++)
                {
                    RpcCurves[i] = engine.GetRpcIndex(soundReader.ReadUInt32());
                }

                // Just in case seek to the right spot.
                soundReader.BaseStream.Seek(current + dataLength, SeekOrigin.Begin);
            }

            if (!hasDSPs)
            {
                _useReverb = false;
            }
            else
            {
                // The file format for this seems to follow the pattern for
                // the RPC curves above, but in this case XACT only supports
                // a single effect...  Microsoft Reverb... so just set it.
                _useReverb = true;
                soundReader.BaseStream.Seek(7, SeekOrigin.Current);
            }

            if (_complexSound)
            {
                _soundClips = new XactClip[numClips];
                for (int i = 0; i < numClips; i++)
                {
                    _soundClips[i] = new XactClip(soundBank, soundReader, _useReverb);
                }
            }

            var category = engine.Categories[_categoryID];

            category.AddSound(this);
        }
Example #7
0
        public XactClip(SoundBank soundBank, BinaryReader clipReader)
        {
            State = SoundState.Stopped;

            var volumeDb = XactHelpers.ParseDecibels(clipReader.ReadByte());

            _defaultVolume = XactHelpers.ParseVolumeFromDecibels(volumeDb);
            var clipOffset = clipReader.ReadUInt32();

            // Unknown!
            clipReader.ReadUInt32();

            var oldPosition = clipReader.BaseStream.Position;

            clipReader.BaseStream.Seek(clipOffset, SeekOrigin.Begin);

            var numEvents = clipReader.ReadByte();

            _events = new ClipEvent[numEvents];

            for (var i = 0; i < numEvents; i++)
            {
                var eventInfo    = clipReader.ReadUInt32();
                var randomOffset = clipReader.ReadUInt16() * 0.001f;

                // TODO: eventInfo still has 11 bits that are unknown!
                var eventId   = eventInfo & 0x1F;
                var timeStamp = ((eventInfo >> 5) & 0xFFFF) * 0.001f;
                var unknown   = eventInfo >> 21;

                switch (eventId)
                {
                case 0:
                    // Stop Event
                    throw new NotImplementedException("Stop event");

                case 1:
                {
                    // Unknown!
                    clipReader.ReadByte();

                    // Event flags
                    var eventFlags       = clipReader.ReadByte();
                    var playRelease      = (eventFlags & 0x01) == 0x01;
                    var panEnabled       = (eventFlags & 0x02) == 0x02;
                    var useCenterSpeaker = (eventFlags & 0x04) == 0x04;

                    int trackIndex    = clipReader.ReadUInt16();
                    int waveBankIndex = clipReader.ReadByte();
                    var loopCount     = clipReader.ReadByte();
                    var panAngle      = clipReader.ReadUInt16() / 100.0f;
                    var panArc        = clipReader.ReadUInt16() / 100.0f;

                    _events[i] = new PlayWaveEvent(
                        this,
                        timeStamp,
                        randomOffset,
                        soundBank,
                        new[] { waveBankIndex },
                        new[] { trackIndex },
                        null,
                        0,
                        VariationType.Ordered,
                        null,
                        null,
                        loopCount,
                        false);

                    break;
                }

                case 3:
                {
                    // Unknown!
                    clipReader.ReadByte();

                    // Event flags
                    var eventFlags       = clipReader.ReadByte();
                    var playRelease      = (eventFlags & 0x01) == 0x01;
                    var panEnabled       = (eventFlags & 0x02) == 0x02;
                    var useCenterSpeaker = (eventFlags & 0x04) == 0x04;

                    var loopCount = clipReader.ReadByte();
                    var panAngle  = clipReader.ReadUInt16() / 100.0f;
                    var panArc    = clipReader.ReadUInt16() / 100.0f;

                    // The number of tracks for the variations.
                    var numTracks = clipReader.ReadUInt16();

                    // Not sure what most of this is.
                    var moreFlags     = clipReader.ReadByte();
                    var newWaveOnLoop = (moreFlags & 0x40) == 0x40;

                    // The variation playlist type seems to be
                    // stored in the bottom 4bits only.
                    var variationType = (VariationType)(moreFlags & 0x0F);

                    // Unknown!
                    clipReader.ReadBytes(5);

                    // Read in the variation playlist.
                    var waveBanks    = new int[numTracks];
                    var tracks       = new int[numTracks];
                    var weights      = new byte[numTracks];
                    var totalWeights = 0;
                    for (var j = 0; j < numTracks; j++)
                    {
                        tracks[j]    = clipReader.ReadUInt16();
                        waveBanks[j] = clipReader.ReadByte();
                        var minWeight = clipReader.ReadByte();
                        var maxWeight = clipReader.ReadByte();
                        weights[j]    = (byte)(maxWeight - minWeight);
                        totalWeights += weights[j];
                    }

                    _events[i] = new PlayWaveEvent(
                        this,
                        timeStamp,
                        randomOffset,
                        soundBank,
                        waveBanks,
                        tracks,
                        weights,
                        totalWeights,
                        variationType,
                        null,
                        null,
                        loopCount,
                        newWaveOnLoop);

                    break;
                }

                case 4:
                {
                    // Unknown!
                    clipReader.ReadByte();

                    // Event flags
                    var eventFlags       = clipReader.ReadByte();
                    var playRelease      = (eventFlags & 0x01) == 0x01;
                    var panEnabled       = (eventFlags & 0x02) == 0x02;
                    var useCenterSpeaker = (eventFlags & 0x04) == 0x04;

                    int trackIndex    = clipReader.ReadUInt16();
                    int waveBankIndex = clipReader.ReadByte();
                    var loopCount     = clipReader.ReadByte();
                    var panAngle      = clipReader.ReadUInt16() / 100.0f;
                    var panArc        = clipReader.ReadUInt16() / 100.0f;

                    // Pitch variation range
                    var minPitch = clipReader.ReadInt16() / 1000.0f;
                    var maxPitch = clipReader.ReadInt16() / 1000.0f;

                    // Volume variation range
                    var minVolume = XactHelpers.ParseVolumeFromDecibels(clipReader.ReadByte());
                    var maxVolume = XactHelpers.ParseVolumeFromDecibels(clipReader.ReadByte());

                    // Filter variation
                    var minFrequency = clipReader.ReadSingle() / 1000.0f;
                    var maxFrequency = clipReader.ReadSingle() / 1000.0f;
                    var minQ         = clipReader.ReadSingle();
                    var maxQ         = clipReader.ReadSingle();

                    // Unknown!
                    clipReader.ReadByte();

                    _events[i] = new PlayWaveEvent(
                        this,
                        timeStamp,
                        randomOffset,
                        soundBank,
                        new[] { waveBankIndex },
                        new[] { trackIndex },
                        null,
                        0,
                        VariationType.Ordered,
                        new Vector2(minVolume, maxVolume - minVolume),
                        new Vector2(minPitch, maxPitch - minPitch),
                        loopCount,
                        false);

                    break;
                }

                case 6:
                {
                    // Unknown!
                    clipReader.ReadByte();

                    // Event flags
                    var eventFlags       = clipReader.ReadByte();
                    var playRelease      = (eventFlags & 0x01) == 0x01;
                    var panEnabled       = (eventFlags & 0x02) == 0x02;
                    var useCenterSpeaker = (eventFlags & 0x04) == 0x04;

                    var loopCount = clipReader.ReadByte();
                    var panAngle  = clipReader.ReadUInt16() / 100.0f;
                    var panArc    = clipReader.ReadUInt16() / 100.0f;

                    // Pitch variation range
                    var minPitch = clipReader.ReadInt16() / 1000.0f;
                    var maxPitch = clipReader.ReadInt16() / 1000.0f;

                    // Volume variation range
                    var minVolume = XactHelpers.ParseVolumeFromDecibels(clipReader.ReadByte());
                    var maxVolume = XactHelpers.ParseVolumeFromDecibels(clipReader.ReadByte());

                    // Filter variation range
                    var minFrequency = clipReader.ReadSingle() / 1000.0f;
                    var maxFrequency = clipReader.ReadSingle() / 1000.0f;
                    var minQ         = clipReader.ReadSingle();
                    var maxQ         = clipReader.ReadSingle();

                    // Unknown!
                    clipReader.ReadByte();

                    // TODO: Still has unknown bits!
                    var variationFlags = clipReader.ReadByte();

                    // Enable pitch variation
                    Vector2?pitchVar = null;
                    if ((variationFlags & 0x10) == 0x10)
                    {
                        pitchVar = new Vector2(minPitch, maxPitch - minPitch);
                    }

                    // Enable volume variation
                    Vector2?volumeVar = null;
                    if ((variationFlags & 0x20) == 0x20)
                    {
                        volumeVar = new Vector2(minVolume, maxVolume - minVolume);
                    }

                    // Enable filter variation
                    Vector4?filterVar = null;
                    if ((variationFlags & 0x40) == 0x40)
                    {
                        filterVar = new Vector4(minFrequency, maxFrequency - minFrequency, minQ, maxQ - minQ);
                    }

                    // The number of tracks for the variations.
                    var numTracks = clipReader.ReadUInt16();

                    // Not sure what most of this is.
                    var moreFlags     = clipReader.ReadByte();
                    var newWaveOnLoop = (moreFlags & 0x40) == 0x40;

                    // The variation playlist type seems to be
                    // stored in the bottom 4bits only.
                    var variationType = (VariationType)(moreFlags & 0x0F);

                    // Unknown!
                    clipReader.ReadBytes(5);

                    // Read in the variation playlist.
                    var waveBanks    = new int[numTracks];
                    var tracks       = new int[numTracks];
                    var weights      = new byte[numTracks];
                    var totalWeights = 0;
                    for (var j = 0; j < numTracks; j++)
                    {
                        tracks[j]    = clipReader.ReadUInt16();
                        waveBanks[j] = clipReader.ReadByte();
                        var minWeight = clipReader.ReadByte();
                        var maxWeight = clipReader.ReadByte();
                        weights[j]    = (byte)(maxWeight - minWeight);
                        totalWeights += weights[j];
                    }

                    _events[i] = new PlayWaveEvent(
                        this,
                        timeStamp,
                        randomOffset,
                        soundBank,
                        waveBanks,
                        tracks,
                        weights,
                        totalWeights,
                        variationType,
                        volumeVar,
                        pitchVar,
                        loopCount,
                        newWaveOnLoop);

                    break;
                }

                case 7:
                    // Pitch Event
                    throw new NotImplementedException("Pitch event");

                case 8:
                {
                    // Unknown!
                    clipReader.ReadBytes(2);

                    // Event flags
                    var eventFlags = clipReader.ReadByte();
                    var isAdd      = (eventFlags & 0x01) == 0x01;

                    // The replacement or additive volume.
                    var decibles = clipReader.ReadSingle() / 100.0f;
                    var volume   = XactHelpers.ParseVolumeFromDecibels(decibles + (isAdd ? volumeDb : 0));

                    // Unknown!
                    clipReader.ReadBytes(9);

                    _events[i] = new VolumeEvent(this,
                                                 timeStamp,
                                                 randomOffset,
                                                 volume);
                    break;
                }

                case 17:
                    // Volume Repeat Event
                    throw new NotImplementedException("Volume repeat event");

                case 9:
                    // Marker Event
                    throw new NotImplementedException("Marker event");

                default:
                    throw new NotSupportedException("Unknown event " + eventId);
                }
            }

            clipReader.BaseStream.Seek(oldPosition, SeekOrigin.Begin);
        }