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; }
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); }
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); }
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 }
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); }
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); }
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); }