private void PlayWave(PlayWaveEvent evt, float?prevVolume = null, float?prevPitch = null) { SoundEffectInstance sfi = evt.GenerateInstance( INTERNAL_activeSound.Volume, INTERNAL_activeSound.Pitch, INTERNAL_eventLoops[evt], prevVolume, prevPitch ); if (sfi != null) { if (INTERNAL_isPositional) { sfi.Apply3D(INTERNAL_listener, INTERNAL_emitter); } foreach (uint curDSP in INTERNAL_activeSound.DSPCodes) { // FIXME: This only applies the last DSP! sfi.INTERNAL_applyReverb( INTERNAL_baseEngine.INTERNAL_getDSP(curDSP) ); } INTERNAL_instancePool.Add(sfi); INTERNAL_instanceVolumes.Add(sfi.Volume); INTERNAL_instancePitches.Add(sfi.Pitch); INTERNAL_waveEventSounds.Add(sfi, evt); INTERNAL_rpcTrackVolumes.Add(1.0f); INTERNAL_rpcTrackPitches.Add(0.0f); sfi.Play(); } }
public XACTClip(ushort track, byte waveBank) { INTERNAL_events = new XACTEvent[1]; INTERNAL_events[0] = new PlayWaveEvent( 0, new ushort[] { track }, new byte[] { waveBank }, 0, 0, 1.0, 1.0, 0, 0, new byte[] { 0xFF } ); }
internal void PlayWave( EventInstance eventInstance, double?prevVolume = null, short?prevPitch = null ) { PlayWaveEventInstance playWaveEventInstance = (PlayWaveEventInstance)eventInstance; PlayWaveEvent evt = (PlayWaveEvent)eventInstance.Event; double finalVolume; short finalPitch; SoundEffectInstance sfi = evt.GenerateInstance( INTERNAL_activeSound.Sound.Volume, INTERNAL_activeSound.Sound.Pitch, playWaveEventInstance.LoopCount, prevVolume, prevPitch, out finalVolume, out finalPitch ); if (sfi != null) { if (INTERNAL_isPositional) { sfi.Apply3D(INTERNAL_listener, INTERNAL_emitter); } foreach (uint curDSP in INTERNAL_activeSound.Sound.DSPCodes) { // FIXME: This only applies the last DSP! sfi.INTERNAL_applyReverb( INTERNAL_baseEngine.INTERNAL_getDSP(curDSP) ); } INTERNAL_instancePool.Add(sfi); INTERNAL_instanceVolumes.Add(finalVolume); INTERNAL_instancePitches.Add(finalPitch); INTERNAL_playWaveEventBySound.Add(sfi, playWaveEventInstance); INTERNAL_rpcTrackVolumes.Add(0.0f); INTERNAL_rpcTrackPitches.Add(0.0f); sfi.Play(); } }
public XACTClip(ushort track, byte waveBank) { Events = new XACTEvent[1]; Events[0] = new PlayWaveEvent( 0, new ushort[] { track }, new byte[] { waveBank }, 0, 0, 1.0, 1.0, 0xFF, 0, 0, false, new byte[] { 0xFF } ); }
private void PlayWave(PlayWaveEvent evt) { SoundEffectInstance sfi = evt.GenerateInstance( INTERNAL_activeSound.Volume, INTERNAL_activeSound.Pitch, INTERNAL_eventLoops[evt] ); if (sfi != null) { if (INTERNAL_isPositional) { sfi.Apply3D(INTERNAL_listener, INTERNAL_emitter); } foreach (uint curDSP in INTERNAL_activeSound.DSPCodes) { // FIXME: This only applies the last DSP! sfi.INTERNAL_applyReverb( INTERNAL_baseEngine.INTERNAL_getDSP(curDSP) ); } INTERNAL_instancePool.Add(sfi); INTERNAL_instanceVolumes.Add(sfi.Volume); INTERNAL_instancePitches.Add(sfi.Pitch); INTERNAL_waveEventSounds.Add(sfi, evt); INTERNAL_rpcTrackVolumes.Add(1.0f); INTERNAL_rpcTrackPitches.Add(0.0f); sfi.Play(); } }
public XACTClip(BinaryReader reader, double clipVolume) { // Number of XACT Events INTERNAL_events = new XACTEvent[reader.ReadByte()]; for (int i = 0; i < INTERNAL_events.Length; i += 1) { // Full Event information uint eventInfo = reader.ReadUInt32(); // XACT Event Type, Timestamp uint eventType = eventInfo & 0x0000001F; uint eventTimestamp = (eventInfo >> 5) & 0x0000FFFF; // uint eventUnknown = eventInfo >> 21; // Random offset, unused reader.ReadUInt16(); // Load the Event if (eventType == 0) { // TODO: Codename OhGodNo // Stop Event } else if (eventType == 1) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank Track Index ushort track = reader.ReadUInt16(); // WaveBank Index byte waveBank = reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, new ushort[] { track }, new byte[] { waveBank }, 0, 0, clipVolume, clipVolume, -1, loopCount, 0, new byte[] { 0xFF } ); } else if (eventType == 3) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); /* Variation Playlist Type. * First 4 bytes indicates Variation Type. * Next 4 bytes appear to indicate New Variation On Loop. * The rest is currently unknown. * -flibit */ ushort variationType = (ushort)(reader.ReadUInt16() & 0x000F); // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte)(maxWeight - minWeight); } // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, tracks, waveBanks, 0, 0, clipVolume, clipVolume, -1, loopCount, variationType, weights ); } else if (eventType == 4) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank track ushort track = reader.ReadUInt16(); // WaveBank index, unconfirmed byte waveBank = reader.ReadByte(); // Loop Count, unconfirmed byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Pitch Variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume Variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Frequency Variation, unusued reader.ReadSingle(); reader.ReadSingle(); // Q Factor Variation, unused reader.ReadSingle(); reader.ReadSingle(); // Filter Type byte filterType = reader.ReadByte(); // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, new ushort[] { track }, new byte[] { waveBank }, minPitch, maxPitch, minVolume, maxVolume, (int)filterType, loopCount, 0, new byte[] { 0xFF } ); } else if (eventType == 6) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Pitch variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Frequency Variation, unusued reader.ReadSingle(); reader.ReadSingle(); // Q Factor Variation, unused reader.ReadSingle(); reader.ReadSingle(); // Filter Type byte filterType = reader.ReadByte(); // Variation flags // FIXME: There's probably more to these flags... byte varFlags = reader.ReadByte(); if ((varFlags & 0x20) != 0x20) { // Throw out the volume variation. minVolume = clipVolume; maxVolume = clipVolume; } if ((varFlags & 0x10) != 0x10) { // Throw out the pitch variation minPitch = 0; maxPitch = 0; } // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); /* Variation Playlist Type. * First 4 bytes indicates Variation Type. * Next 4 bytes appear to indicate New Variation On Loop. * The rest is currently unknown. * -flibit */ ushort variationType = (ushort)(reader.ReadUInt16() & 0x000F); // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte)(maxWeight - minWeight); } // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, tracks, waveBanks, minPitch, maxPitch, minVolume, maxVolume, (int)filterType, loopCount, variationType, weights ); } else if (eventType == 7) { // TODO: Codename OhGodNo -flibit // Pitch Event } else if (eventType == 8) { // Unknown values reader.ReadBytes(2); /* Event Flags * 0x01 = Add, rather than replace * Rest is unknown */ bool addVolume = (reader.ReadByte() & 0x01) == 0x01; // Operand Constant float constant = reader.ReadSingle() / 100.0f; if (addVolume) { constant += (float)clipVolume; } // Unknown values reader.ReadBytes(8); INTERNAL_events[i] = new SetVolumeEvent( eventTimestamp, XACTCalculator.CalculateAmplitudeRatio(constant) ); } else if (eventType == 15) { // TODO: Codename OhGodNo -flibit // Unknown Event! } else if (eventType == 17) { // TODO: Codename OhGodNo -flibit // Volume Repeat Event } else { /* TODO: All XACT Events. * The following type information is based on * third-party contributions: * Type 9 - Marker Event * -flibit */ throw new Exception( "EVENT TYPE " + eventType.ToString() + " NOT IMPLEMENTED!" ); } } }
public XACTClip(BinaryReader reader, double clipVolume) { // Number of XACT Events INTERNAL_events = new XACTEvent[reader.ReadByte()]; for (int i = 0; i < INTERNAL_events.Length; i += 1) { // Full Event information uint eventInfo = reader.ReadUInt32(); // XACT Event Type, Timestamp uint eventType = eventInfo & 0x0000001F; uint eventTimestamp = (eventInfo >> 5) & 0x0000FFFF; // uint eventUnknown = eventInfo >> 21; // Random offset, unused reader.ReadUInt16(); // Load the Event if (eventType == 1) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank Track Index ushort track = reader.ReadUInt16(); // WaveBank Index byte waveBank = reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, new ushort[] { track }, new byte[] { waveBank }, 0, 0, clipVolume, clipVolume, loopCount, 0, new byte[] { 0xFF } ); } else if (eventType == 3) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); /* Variation Playlist Type. * First 4 bytes indicates Variation Type. * Next 4 bytes appear to indicate New Variation On Loop. * The rest is currently unknown. * -flibit */ ushort variationType = (ushort) (reader.ReadUInt16() & 0x000F); // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte) (maxWeight - minWeight); } // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, tracks, waveBanks, 0, 0, clipVolume, clipVolume, loopCount, variationType, weights ); } else if (eventType == 4) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank track ushort track = reader.ReadUInt16(); // WaveBank index, unconfirmed byte waveBank = reader.ReadByte(); // Loop Count, unconfirmed byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Pitch Variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume Variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Frequency Variation, unusued reader.ReadSingle(); reader.ReadSingle(); // Q Factor Variation, unused reader.ReadSingle(); reader.ReadSingle(); // Unknown value reader.ReadByte(); // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, new ushort[] { track }, new byte[] { waveBank }, minPitch, maxPitch, minVolume, maxVolume, loopCount, 0, new byte[] { 0xFF } ); } else if (eventType == 6) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Pitch variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Frequency Variation, unusued reader.ReadSingle(); reader.ReadSingle(); // Q Factor Variation, unused reader.ReadSingle(); reader.ReadSingle(); // Unknown value reader.ReadByte(); // Variation flags // FIXME: There's probably more to these flags... byte varFlags = reader.ReadByte(); if ((varFlags & 0x20) != 0x20) { // Throw out the volume variation. minVolume = clipVolume; maxVolume = clipVolume; } if ((varFlags & 0x10) != 0x10) { // Throw out the pitch variation minPitch = 0; maxPitch = 0; } // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); /* Variation Playlist Type. * First 4 bytes indicates Variation Type. * Next 4 bytes appear to indicate New Variation On Loop. * The rest is currently unknown. * -flibit */ ushort variationType = (ushort) (reader.ReadUInt16() & 0x000F); // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte) (maxWeight - minWeight); } // Finally. INTERNAL_events[i] = new PlayWaveEvent( eventTimestamp, tracks, waveBanks, minPitch, maxPitch, minVolume, maxVolume, loopCount, variationType, weights ); } else if (eventType == 8) { // Unknown values reader.ReadBytes(2); /* Event Flags * 0x01 = Add, rather than replace * Rest is unknown */ bool addVolume = (reader.ReadByte() & 0x01) == 0x01; // Operand Constant float constant = reader.ReadSingle() / 100.0f; if (addVolume) { constant += (float) clipVolume; } // Unknown values reader.ReadBytes(8); INTERNAL_events[i] = new SetVolumeEvent( eventTimestamp, XACTCalculator.CalculateAmplitudeRatio(constant) ); } else { /* TODO: All XACT Events. * The following type information is based on * third-party contributions: * Type 0 - Stop Event * Type 7 - Pitch Event * Type 9 - Marker Event * Type 17 - Volume Repeat Event * -flibit */ throw new Exception( "EVENT TYPE " + eventType.ToString() + " NOT IMPLEMENTED!" ); } } }
public XACTClip(ushort track, byte waveBank) { INTERNAL_events = new XACTEvent[1]; INTERNAL_events[0] = new PlayWaveEvent( 0, new ushort[] { track }, new byte[] { waveBank }, 0, 0, 1.0, 1.0, 0, 0, new byte[] { 0xFF } ); }
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); }
public XACTClip(ushort track, byte waveBank) { Events = new XACTEvent[1]; Events[0] = new PlayWaveEvent( 0, new ushort[] { track }, new byte[] { waveBank }, 0, 0, 1.0, 1.0, 0xFF, 0, 0, false, new byte[] { 0xFF } ); }
public XactClip (SoundBank soundBank, BinaryReader clipReader, uint clipOffset) { var oldPosition = clipReader.BaseStream.Position; clipReader.BaseStream.Seek(clipOffset, SeekOrigin.Begin); byte numEvents = clipReader.ReadByte(); _events = new ClipEvent[numEvents]; for (int 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.ReadUInt16(); int trackIndex = clipReader.ReadUInt16(); int waveBankIndex = clipReader.ReadByte(); var loopCount = clipReader.ReadByte(); // if loopCount == 255 its an infinite loop // otherwise it loops n times.. // Unknown! clipReader.ReadUInt16(); clipReader.ReadUInt16(); _events[i] = new PlayWaveEvent( this, timeStamp, randomOffset, soundBank, new[] { waveBankIndex }, new[] { trackIndex }, VariationType.Ordered, loopCount == 255); break; } case 3: { // Unknown! clipReader.ReadByte(); // Event flags clipReader.ReadByte(); // Unknown! clipReader.ReadBytes(5); // The number of tracks for the variations. int numTracks = clipReader.ReadUInt16(); // The variation playlist type seems to be // stored in the bottom 4bits only. var variationType = clipReader.ReadUInt16() & 0x000F; // Unknown! clipReader.ReadBytes(4); // Read in the variation playlist. var waveBanks = new int[numTracks]; var tracks = new int[numTracks]; var weights = new byte[numTracks]; 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); } _events[i] = new PlayWaveEvent( this, timeStamp, randomOffset, soundBank, waveBanks, tracks, (VariationType)variationType, false); break; } case 7: // Pitch Event throw new NotImplementedException("Pitch event"); case 8: // Volume Event throw new NotImplementedException("Volume event"); case 9: // Marker Event throw new NotImplementedException("Marker event"); default: throw new NotSupportedException("Unknown event " + eventId); } } clipReader.BaseStream.Seek (oldPosition, SeekOrigin.Begin); }
internal bool INTERNAL_update() { // If we're not running, save some instructions... if (!INTERNAL_timer.IsRunning) { return(true); } elapsedFrames += 1; // User control updates if (INTERNAL_data.IsUserControlled) { string varName = INTERNAL_data.UserControlVariable; if (INTERNAL_userControlledPlaying && (INTERNAL_baseEngine.INTERNAL_isGlobalVariable(varName) ? !MathHelper.WithinEpsilon(INTERNAL_controlledValue, INTERNAL_baseEngine.GetGlobalVariable(varName)) : !MathHelper.WithinEpsilon(INTERNAL_controlledValue, GetVariable(INTERNAL_data.UserControlVariable)))) { // TODO: Crossfading foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Stop(); sfi.Dispose(); } INTERNAL_instancePool.Clear(); INTERNAL_instanceVolumes.Clear(); INTERNAL_instancePitches.Clear(); INTERNAL_rpcTrackVolumes.Clear(); INTERNAL_rpcTrackPitches.Clear(); if (!INTERNAL_calculateNextSound()) { // Nothing to play, bail. return(true); } INTERNAL_activeSound.InitializeClips(); INTERNAL_timer.Stop(); INTERNAL_timer.Reset(); INTERNAL_timer.Start(); } if (INTERNAL_activeSound == null) { return(INTERNAL_userControlledPlaying); } } // Trigger events for each track foreach (XACTClipInstance clip in INTERNAL_activeSound.Clips) { // Play events when the timestamp has been hit. for (int i = 0; i < clip.Events.Count; i += 1) { EventInstance evt = clip.Events[i]; if (!evt.Played && INTERNAL_timer.ElapsedMilliseconds > evt.Timestamp) { evt.Apply( this, null, INTERNAL_timer.ElapsedMilliseconds / 1000.0f ); } } } // Clear out sound effect instances as they finish for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { if (INTERNAL_instancePool[i].State == SoundState.Stopped) { // Get the event that spawned this instance... PlayWaveEventInstance evtInstance = INTERNAL_playWaveEventBySound[INTERNAL_instancePool[i]]; double prevVolume = INTERNAL_instanceVolumes[i]; short prevPitch = INTERNAL_instancePitches[i]; // Then delete all the guff INTERNAL_playWaveEventBySound.Remove(INTERNAL_instancePool[i]); INTERNAL_instancePool[i].Dispose(); INTERNAL_instancePool.RemoveAt(i); INTERNAL_instanceVolumes.RemoveAt(i); INTERNAL_instancePitches.RemoveAt(i); INTERNAL_rpcTrackVolumes.RemoveAt(i); INTERNAL_rpcTrackPitches.RemoveAt(i); // Increment the loop counter, try to get another loop evtInstance.LoopCount += 1; PlayWave(evtInstance, prevVolume, prevPitch); // Removed a wave, have to step back... i -= 1; } } // Fade in/out float fadePerc = 1.0f; if (INTERNAL_fadeMode != FadeMode.None) { if (INTERNAL_fadeMode == FadeMode.FadeOut) { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = ( INTERNAL_fadeEnd - ( INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart ) ) / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc <= 0.0f) { Stop(AudioStopOptions.Immediate); INTERNAL_fadeMode = FadeMode.None; return(false); } } else if (INTERNAL_fadeMode == FadeMode.FadeIn) { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = INTERNAL_timer.ElapsedMilliseconds / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc > 1.0f) { fadePerc = 1.0f; INTERNAL_fadeMode = FadeMode.None; } } else if (INTERNAL_fadeMode == FadeMode.ReleaseRpc) { float releasePerc = ( INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart ) / (float)INTERNAL_maxRpcReleaseTime; if (releasePerc > 1.0f) { Stop(AudioStopOptions.Immediate); INTERNAL_fadeMode = FadeMode.None; return(false); } } else { throw new NotImplementedException("Unsupported FadeMode!"); } } // If everything has been played and finished, we're done here. if (INTERNAL_instancePool.Count == 0) { bool allPlayed = true; foreach (XACTClipInstance clipInstance in INTERNAL_activeSound.Clips) { foreach (EventInstance evt in clipInstance.Events) { if (!evt.Played) { allPlayed = false; break; } } } if (allPlayed) { // If this is managed, we're done completely. if (INTERNAL_isManaged) { Dispose(); } else { KillCue(); } if (INTERNAL_userControlledPlaying) { // We're "still" "playing" right now... return(true); } IsStopped = true; return(false); } } // RPC updates float rpcVolume = 0.0f; float rpcPitch = 0.0f; float hfGain = 1.0f; float lfGain = 1.0f; for (int i = 0; i < INTERNAL_activeSound.Sound.RPCCodes.Count; i += 1) { // Are we processing an RPC targeting the sound itself rather than a track? bool isSoundRpc = i == 0 && INTERNAL_activeSound.Sound.HasSoundRpcs; // If there is an RPC targeting the sound instance itself, it is handled in rpcVolume/rpcPitch, and the first track is at i-1. int trackRpcIndex = INTERNAL_activeSound.Sound.HasSoundRpcs ? i - 1 : i; // If this RPC Code is for a track that is not active yet, we have nothing to do. if (trackRpcIndex >= INTERNAL_instancePool.Count) { // FIXME: This presumes that tracks start in order, which doesn't have to be true. break; } if (!isSoundRpc) { INTERNAL_rpcTrackVolumes[trackRpcIndex] = 0.0f; INTERNAL_rpcTrackPitches[trackRpcIndex] = 0.0f; } foreach (uint curCode in INTERNAL_activeSound.Sound.RPCCodes[i]) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { float variableValue; if (curRPC.Variable.Equals("AttackTime")) { PlayWaveEvent playWaveEvent = (PlayWaveEvent)INTERNAL_activeSound.Sound.INTERNAL_clips[trackRpcIndex].Events[0]; long elapsedFromPlay = INTERNAL_timer.ElapsedMilliseconds - playWaveEvent.Timestamp; variableValue = elapsedFromPlay; } else if (curRPC.Variable.Equals("ReleaseTime")) { if (INTERNAL_fadeMode == FadeMode.ReleaseRpc) { long elapsedFromStop = INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart; variableValue = elapsedFromStop; } else { variableValue = 0.0f; } } else { variableValue = GetVariable(curRPC.Variable); } result = curRPC.CalculateRPC(variableValue); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } if (curRPC.Parameter == RPCParameter.Volume) { // If this RPC targets the sound instance itself then apply to the dedicated variable. if (isSoundRpc) { rpcVolume += result; } else { INTERNAL_rpcTrackVolumes[trackRpcIndex] += result; } } else if (curRPC.Parameter == RPCParameter.Pitch) { float pitch = result; if (isSoundRpc) { rpcPitch += pitch; } else { INTERNAL_rpcTrackPitches[trackRpcIndex] += pitch; } } else if (curRPC.Parameter == RPCParameter.FilterFrequency) { // FIXME: Just listening to the last RPC! float hf = result / 20000.0f; float lf = 1.0f - hf; if (isSoundRpc) { hfGain = hf; lfGain = lf; } else { throw new NotImplementedException("Per-track filter RPCs!"); } } else { throw new NotImplementedException( "RPC Parameter Type: " + curRPC.Parameter.ToString() ); } } } // Sound effect instance updates for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { /* The final volume should be the combination of the * authored volume, category volume, RPC sound/track * volumes, event volumes, and fade. */ INTERNAL_instancePool[i].Volume = XACTCalculator.CalculateAmplitudeRatio( INTERNAL_instanceVolumes[i] + rpcVolume + INTERNAL_rpcTrackVolumes[i] + eventVolume ) * INTERNAL_category.INTERNAL_volume.Value * fadePerc; /* The final pitch should be the combination of the * authored pitch, RPC sound/track pitches, and event * pitch. * * XACT uses -1200 to 1200 (+/- 12 semitones), * XNA uses -1.0f to 1.0f (+/- 1 octave). */ INTERNAL_instancePool[i].Pitch = ( INTERNAL_instancePitches[i] + rpcPitch + INTERNAL_rpcTrackPitches[i] + eventPitch ) / 1200.0f; /* The final filter is determined by the instance's filter type, * in addition to our calculation of the HF/LF gain values. */ byte fType = INTERNAL_instancePool[i].FilterType; if (fType == 0xFF) { // No-op, no filter! } else if (fType == 0) { INTERNAL_instancePool[i].INTERNAL_applyLowPassFilter(hfGain); } else if (fType == 1) { INTERNAL_instancePool[i].INTERNAL_applyHighPassFilter(lfGain); } else if (fType == 2) { INTERNAL_instancePool[i].INTERNAL_applyBandPassFilter(hfGain, lfGain); } else { throw new InvalidOperationException("Unhandled filter type!"); } // Update 3D position, if applicable if (INTERNAL_isPositional) { INTERNAL_instancePool[i].Apply3D( INTERNAL_listener, INTERNAL_emitter ); } } return(true); }
public PlayWaveEventInstance(PlayWaveEvent evt) : base(evt) { }
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); }
public XACTClip(BinaryReader reader, double clipVolume) { INTERNAL_clipVolume = clipVolume; // Number of XACT Events INTERNAL_events = new XACTEvent[reader.ReadByte()]; for (int i = 0; i < INTERNAL_events.Length; i += 1) { // Full Event information uint eventInfo = reader.ReadUInt32(); // XACT Event Type, Timestamp uint eventType = eventInfo & 0x0000001F; // uint eventTimestamp = (eventInfo >> 5) & 0x0000FFFF; // uint eventUnknown = eventInfo >> 21; // Random offset, unused reader.ReadUInt16(); // Load the Event if (eventType == 1) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank Track Index ushort track = reader.ReadUInt16(); // WaveBank Index byte waveBank = reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Unknown value reader.ReadUInt32(); // Finally. INTERNAL_events[i] = new PlayWaveEvent( new ushort[] { track }, new byte[] { waveBank }, 0, 0, 0.0, 0.0, loopCount, 0, new byte[] { 0xFF } ); } else if (eventType == 3) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Unknown values reader.ReadBytes(5); // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); // Variation Playlist Type ushort variationType = reader.ReadUInt16(); // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte)(maxWeight - minWeight); } // Finally. INTERNAL_events[i] = new PlayWaveEvent( tracks, waveBanks, 0, 0, 0.0, 0.0, 0, variationType, weights ); } else if (eventType == 4) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank track ushort track = reader.ReadUInt16(); // WaveBank index, unconfirmed byte waveBank = reader.ReadByte(); // Loop Count, unconfirmed byte loopCount = reader.ReadByte(); // Unknown values reader.ReadBytes(4); // Pitch Variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume Variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Unknown values reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); reader.ReadByte(); // Finally. INTERNAL_events[i] = new PlayWaveEvent( new ushort[] { track }, new byte[] { waveBank }, minPitch, maxPitch, minVolume, maxVolume, loopCount, 0, new byte[] { 0xFF } ); } else if (eventType == 6) { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Unknown values reader.ReadBytes(5); // Pitch variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Unknown values reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); reader.ReadByte(); // Variation flags // FIXME: There's probably more to these flags... byte varFlags = reader.ReadByte(); if ((varFlags & 0x20) != 0x20) { // Throw out the volume variation. minVolume = 0.0; maxVolume = 0.0; } if ((varFlags & 0x10) != 0x10) { // Throw out the pitch variation minPitch = 0; maxPitch = 0; } // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); // Variation Playlist Type ushort variationType = reader.ReadUInt16(); // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte)(maxWeight - minWeight); } // Finally. INTERNAL_events[i] = new PlayWaveEvent( tracks, waveBanks, minPitch, maxPitch, minVolume, maxVolume, 0, variationType, weights ); } else if (eventType == 8) { // Unknown values reader.ReadBytes(3); // Operand Constant float constant = reader.ReadSingle(); // Unknown values reader.ReadBytes(8); INTERNAL_events[i] = new SetVolumeEvent( constant ); } else { /* TODO: All XACT Events. * The following type information is based on * third-party contributions: * Type 0 - Stop Event * Type 7 - Pitch Event * Type 9 - Marker Event * -flibit */ throw new Exception( "EVENT TYPE " + eventType.ToString() + " NOT IMPLEMENTED!" ); } } }
internal bool INTERNAL_update() { // If we're not running, save some instructions... if (!INTERNAL_timer.IsRunning) { return(true); } elapsedFrames += 1; // Play events when the timestamp has been hit. for (int i = 0; i < INTERNAL_eventList.Count; i += 1) { if (!INTERNAL_eventPlayed[i] && INTERNAL_timer.ElapsedMilliseconds > INTERNAL_eventList[i].Timestamp) { uint type = INTERNAL_eventList[i].Type; if (type == 1) { PlayWave((PlayWaveEvent)INTERNAL_eventList[i]); } else if (type == 2) { eventVolume = ((SetVolumeEvent)INTERNAL_eventList[i]).GetVolume(); } else if (type == 3) { eventPitch = ((SetPitchEvent)INTERNAL_eventList[i]).GetPitch(); } else { throw new NotImplementedException("Unhandled XACTEvent type!"); } INTERNAL_eventPlayed[i] = true; } } // Clear out sound effect instances as they finish for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { if (INTERNAL_instancePool[i].State == SoundState.Stopped) { // Get the event that spawned this instance... PlayWaveEvent evt = (PlayWaveEvent)INTERNAL_waveEventSounds[INTERNAL_instancePool[i]]; float prevVolume = INTERNAL_instanceVolumes[i]; float prevPitch = INTERNAL_instancePitches[i]; // Then delete all the guff INTERNAL_waveEventSounds.Remove(INTERNAL_instancePool[i]); INTERNAL_instancePool[i].Dispose(); INTERNAL_instancePool.RemoveAt(i); INTERNAL_instanceVolumes.RemoveAt(i); INTERNAL_instancePitches.RemoveAt(i); INTERNAL_rpcTrackVolumes.RemoveAt(i); INTERNAL_rpcTrackPitches.RemoveAt(i); // Increment the loop counter, try to get another loop INTERNAL_eventLoops[evt] += 1; PlayWave(evt, prevVolume, prevPitch); // Removed a wave, have to step back... i -= 1; } } // Fade in/out float fadePerc = 1.0f; if (INTERNAL_fadeMode != FadeMode.None) { if (INTERNAL_fadeMode == FadeMode.FadeOut) { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = (INTERNAL_fadeEnd - (INTERNAL_timer.ElapsedMilliseconds - INTERNAL_fadeStart)) / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc <= 0.0f) { Stop(AudioStopOptions.Immediate); INTERNAL_fadeMode = FadeMode.None; return(false); } } else { if (INTERNAL_category.crossfadeType == CrossfadeType.Linear) { fadePerc = INTERNAL_timer.ElapsedMilliseconds / (float)INTERNAL_fadeEnd; } else { throw new NotImplementedException("Unhandled CrossfadeType!"); } if (fadePerc > 1.0f) { fadePerc = 1.0f; INTERNAL_fadeMode = FadeMode.None; } } } // User control updates if (INTERNAL_data.IsUserControlled) { string varName = INTERNAL_data.UserControlVariable; if (INTERNAL_userControlledPlaying && (INTERNAL_baseEngine.INTERNAL_isGlobalVariable(varName) ? !MathHelper.WithinEpsilon(INTERNAL_controlledValue, INTERNAL_baseEngine.GetGlobalVariable(varName)) : !MathHelper.WithinEpsilon(INTERNAL_controlledValue, GetVariable(INTERNAL_data.UserControlVariable)))) { // TODO: Crossfading foreach (SoundEffectInstance sfi in INTERNAL_instancePool) { sfi.Stop(); sfi.Dispose(); } INTERNAL_instancePool.Clear(); INTERNAL_instanceVolumes.Clear(); INTERNAL_instancePitches.Clear(); INTERNAL_rpcTrackVolumes.Clear(); INTERNAL_rpcTrackPitches.Clear(); if (!INTERNAL_calculateNextSound()) { // Nothing to play, bail. return(true); } INTERNAL_activeSound.GatherEvents(INTERNAL_eventList); foreach (XACTEvent evt in INTERNAL_eventList) { INTERNAL_eventPlayed.Add(false); INTERNAL_eventLoops.Add(evt, 0); } INTERNAL_timer.Stop(); INTERNAL_timer.Reset(); INTERNAL_timer.Start(); } if (INTERNAL_activeSound == null) { return(INTERNAL_userControlledPlaying); } } // If everything has been played and finished, we're done here. if (INTERNAL_instancePool.Count == 0) { bool allPlayed = true; foreach (bool played in INTERNAL_eventPlayed) { if (!played) { allPlayed = false; break; } } if (allPlayed) { // If this is managed, we're done completely. if (INTERNAL_isManaged) { Dispose(); } else { INTERNAL_timer.Stop(); INTERNAL_timer.Reset(); INTERNAL_category.INTERNAL_removeActiveCue(this); } return(INTERNAL_userControlledPlaying); } } // RPC updates float rpcVolume = 1.0f; float rpcPitch = 0.0f; float hfGain = 1.0f; float lfGain = 1.0f; for (int i = 0; i < INTERNAL_activeSound.RPCCodes.Count; i += 1) { if (i > INTERNAL_instancePool.Count) { break; } if (i > 0) { INTERNAL_rpcTrackVolumes[i - 1] = 1.0f; INTERNAL_rpcTrackPitches[i - 1] = 0.0f; } foreach (uint curCode in INTERNAL_activeSound.RPCCodes[i]) { RPC curRPC = INTERNAL_baseEngine.INTERNAL_getRPC(curCode); float result; if (!INTERNAL_baseEngine.INTERNAL_isGlobalVariable(curRPC.Variable)) { result = curRPC.CalculateRPC(GetVariable(curRPC.Variable)); } else { // It's a global variable we're looking for! result = curRPC.CalculateRPC( INTERNAL_baseEngine.GetGlobalVariable( curRPC.Variable ) ); } if (curRPC.Parameter == RPCParameter.Volume) { float vol = XACTCalculator.CalculateAmplitudeRatio(result / 100.0); if (i == 0) { rpcVolume *= vol; } else { INTERNAL_rpcTrackVolumes[i - 1] *= vol; } } else if (curRPC.Parameter == RPCParameter.Pitch) { float pitch = result / 1000.0f; if (i == 0) { rpcPitch += pitch; } else { INTERNAL_rpcTrackPitches[i - 1] += pitch; } } else if (curRPC.Parameter == RPCParameter.FilterFrequency) { // FIXME: Just listening to the last RPC! float hf = result / 20000.0f; float lf = 1.0f - hf; if (i == 0) { hfGain = hf; lfGain = lf; } else { throw new NotImplementedException("Per-track filter RPCs!"); } } else { throw new NotImplementedException("RPC Parameter Type: " + curRPC.Parameter.ToString()); } } } // Sound effect instance updates for (int i = 0; i < INTERNAL_instancePool.Count; i += 1) { /* The final volume should be the combination of the * authored volume, category volume, RPC/Event volumes, and fade. */ INTERNAL_instancePool[i].Volume = ( INTERNAL_instanceVolumes[i] * INTERNAL_category.INTERNAL_volume.Value * rpcVolume * INTERNAL_rpcTrackVolumes[i] * eventVolume * fadePerc ); /* The final pitch should be the combination of the * authored pitch and RPC/Event pitch results. */ INTERNAL_instancePool[i].Pitch = ( INTERNAL_instancePitches[i] + rpcPitch + eventPitch + INTERNAL_rpcTrackPitches[i] ); /* The final filter is determined by the instance's filter type, * in addition to our calculation of the HF/LF gain values. */ byte fType = INTERNAL_instancePool[i].FilterType; if (fType == 0xFF) { // No-op, no filter! } else if (fType == 0) { INTERNAL_instancePool[i].INTERNAL_applyLowPassFilter(hfGain); } else if (fType == 1) { INTERNAL_instancePool[i].INTERNAL_applyHighPassFilter(lfGain); } else if (fType == 2) { INTERNAL_instancePool[i].INTERNAL_applyBandPassFilter(hfGain, lfGain); } else { throw new InvalidOperationException("Unhandled filter type!"); } // Update 3D position, if applicable if (INTERNAL_isPositional) { INTERNAL_instancePool[i].Apply3D( INTERNAL_listener, INTERNAL_emitter ); } } return(true); }
public XactClip(SoundBank soundBank, BinaryReader clipReader, uint clipOffset) { var oldPosition = clipReader.BaseStream.Position; clipReader.BaseStream.Seek(clipOffset, SeekOrigin.Begin); byte numEvents = clipReader.ReadByte(); _events = new ClipEvent[numEvents]; for (int 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.ReadUInt16(); int trackIndex = clipReader.ReadUInt16(); int waveBankIndex = clipReader.ReadByte(); var loopCount = clipReader.ReadByte(); // if loopCount == 255 its an infinite loop // otherwise it loops n times.. // Unknown! clipReader.ReadUInt16(); clipReader.ReadUInt16(); _events[i] = new PlayWaveEvent( this, timeStamp, randomOffset, soundBank, new[] { waveBankIndex }, new[] { trackIndex }, VariationType.Ordered, loopCount == 255); break; } case 3: { // Unknown! clipReader.ReadByte(); // Event flags clipReader.ReadByte(); // Unknown! clipReader.ReadBytes(5); // The number of tracks for the variations. int numTracks = clipReader.ReadUInt16(); // The variation playlist type seems to be // stored in the bottom 4bits only. var variationType = clipReader.ReadUInt16() & 0x000F; // Unknown! clipReader.ReadBytes(4); // Read in the variation playlist. var waveBanks = new int[numTracks]; var tracks = new int[numTracks]; var weights = new byte[numTracks]; 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); } _events[i] = new PlayWaveEvent( this, timeStamp, randomOffset, soundBank, waveBanks, tracks, (VariationType)variationType, false); break; } case 7: // Pitch Event throw new NotImplementedException("Pitch event"); case 8: // Volume Event throw new NotImplementedException("Volume event"); case 9: // Marker Event throw new NotImplementedException("Marker event"); default: throw new NotSupportedException("Unknown event " + eventId); } } clipReader.BaseStream.Seek(oldPosition, SeekOrigin.Begin); }
public XACTClip(BinaryReader reader, double clipVolume, byte filterType) { // Number of XACT Events Events = new XACTEvent[reader.ReadByte()]; for (int i = 0; i < Events.Length; i += 1) { // Full Event information uint eventInfo = reader.ReadUInt32(); // XACT Event Type, Timestamp uint eventType = eventInfo & 0x0000001F; uint eventTimestamp = (eventInfo >> 5) & 0x0000FFFF; // uint eventUnknown = eventInfo >> 21; // Random offset, unused reader.ReadUInt16(); // Load the Event if (eventType == 0) // StopEvent { // TODO: Codename OhGodNo } else if (eventType == 1) // Basic PlayWaveEvent { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank Track Index ushort track = reader.ReadUInt16(); // WaveBank Index byte waveBank = reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Finally. Events[i] = new PlayWaveEvent( eventTimestamp, new ushort[] { track }, new byte[] { waveBank }, 0, 0, clipVolume, clipVolume, filterType, loopCount, false, false, false, false, 0, false, new byte[] { 0xFF } ); } else if (eventType == 3) // PlayWaveEvent with track variation { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); /* Variation Playlist Type. * First 4 bytes indicates Variation Type. * Next 4 bytes appear to indicate New Variation On Loop. * The rest is currently unknown. * -flibit */ ushort variationValues = reader.ReadUInt16(); ushort variationType = (ushort)(variationValues & 0x000F); bool variationOnLoop = (variationValues & 0x00F0) > 0; // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte)(maxWeight - minWeight); } // Finally. Events[i] = new PlayWaveEvent( eventTimestamp, tracks, waveBanks, 0, 0, clipVolume, clipVolume, filterType, loopCount, false, false, false, false, variationType, variationOnLoop, weights ); } else if (eventType == 4) // PlayWaveEvent with effect variation { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // WaveBank track ushort track = reader.ReadUInt16(); // WaveBank index, unconfirmed byte waveBank = reader.ReadByte(); // Loop Count, unconfirmed byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Pitch Variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume Variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Frequency Variation, unusued reader.ReadSingle(); reader.ReadSingle(); // Q Factor Variation, unused reader.ReadSingle(); reader.ReadSingle(); // Variation On Loop flags ushort varFlags = reader.ReadUInt16(); if ((varFlags & 0x1000) == 0) { minPitch = 0; maxPitch = 0; } if ((varFlags & 0x2000) == 0) { minVolume = clipVolume; maxVolume = clipVolume; } // varFlags & 0xC000 is freq/qfactor, always together bool pitchVarLoop = (varFlags & 0x0100) > 0; bool volumeVarLoop = (varFlags & 0x0200) > 0; // varFlags & 0x0C00 is freq/qfactor loop, always together bool pitchVarAdd = (varFlags & 0x0004) > 0; bool volumeVarAdd = (varFlags & 0x0001) > 0; // varFlags & 0x0050 is freq/qfactor add, can be separate // Finally. Events[i] = new PlayWaveEvent( eventTimestamp, new ushort[] { track }, new byte[] { waveBank }, minPitch, maxPitch, minVolume, maxVolume, filterType, loopCount, pitchVarLoop, pitchVarAdd, volumeVarLoop, volumeVarAdd, 0, false, new byte[] { 0xFF } ); } else if (eventType == 6) // PlayWaveEvent with track/effect variation { // Unknown value reader.ReadByte(); /* Event Flags * 0x01 = Break Loop * 0x02 = Use Speaker Position * 0x04 = Use Center Speaker * 0x08 = New Speaker Position On Loop */ reader.ReadByte(); // Number of times to loop wave (255 is infinite) byte loopCount = reader.ReadByte(); // Speaker position angle/arc, unused reader.ReadUInt16(); reader.ReadUInt16(); // Pitch variation short minPitch = reader.ReadInt16(); short maxPitch = reader.ReadInt16(); // Volume variation double minVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); double maxVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Frequency Variation, unusued reader.ReadSingle(); reader.ReadSingle(); // Q Factor Variation, unused reader.ReadSingle(); reader.ReadSingle(); // Variation On Loop flags ushort varFlags = reader.ReadUInt16(); if ((varFlags & 0x1000) == 0) { minPitch = 0; maxPitch = 0; } if ((varFlags & 0x2000) == 0) { minVolume = clipVolume; maxVolume = clipVolume; } // varFlags & 0xC000 is freq/qfactor, always together bool pitchVarLoop = (varFlags & 0x0100) > 0; bool volumeVarLoop = (varFlags & 0x0200) > 0; // varFlags & 0x0C00 is freq/qfactor loop, always together bool pitchVarAdd = (varFlags & 0x0004) > 0; bool volumeVarAdd = (varFlags & 0x0001) > 0; // varFlags & 0x0050 is freq/qfactor add, can be separate // Number of WaveBank tracks ushort numTracks = reader.ReadUInt16(); /* Variation Playlist Type. * First 4 bytes indicates Variation Type. * Next 4 bytes appear to indicate New Variation On Loop. * The rest is currently unknown. * -flibit */ ushort variationValues = reader.ReadUInt16(); ushort variationType = (ushort)(variationValues & 0x000F); bool variationOnLoop = (variationValues & 0x00F0) > 0; // Unknown values reader.ReadBytes(4); // Obtain WaveBank track information ushort[] tracks = new ushort[numTracks]; byte[] waveBanks = new byte[numTracks]; byte[] weights = new byte[numTracks]; for (ushort j = 0; j < numTracks; j += 1) { tracks[j] = reader.ReadUInt16(); waveBanks[j] = reader.ReadByte(); byte minWeight = reader.ReadByte(); byte maxWeight = reader.ReadByte(); weights[j] = (byte)(maxWeight - minWeight); } // Finally. Events[i] = new PlayWaveEvent( eventTimestamp, tracks, waveBanks, minPitch, maxPitch, minVolume, maxVolume, filterType, loopCount, pitchVarLoop, pitchVarAdd, volumeVarLoop, volumeVarAdd, variationType, variationOnLoop, weights ); } else if (eventType == 7) // SetPitchEvent { // Unknown values reader.ReadBytes(2); /* Event Flags * 0x08 = Min/Max Values * Rest is unknown */ bool minMax = (reader.ReadByte() & 0x08) == 0x08; // Min/Max Random float min = reader.ReadSingle() / 1000.0f; float max; if (minMax) { max = reader.ReadSingle() / 1000.0f; } else { max = min; } // FIXME: Any more...? -flibit Events[i] = new SetPitchEvent( eventTimestamp, min, max ); } else if (eventType == 8) // SetVolumeEvent { // Unknown values reader.ReadBytes(2); /* Event Flags * 0x08 = Min/Max Values * 0x01 = Add, rather than replace * Rest is unknown */ byte flags = reader.ReadByte(); bool addVolume = (flags & 0x01) == 0x01; bool minMax = (flags & 0x08) == 0x08; // Operand Constant float min = reader.ReadSingle() / 100.0f; float max; if (minMax) { max = reader.ReadSingle() / 100.0f; // Unknown bytes reader.ReadBytes(5); } else { max = min; // Unknown values reader.ReadBytes(8); } if (addVolume) { min += (float)clipVolume; max += (float)clipVolume; } Events[i] = new SetVolumeEvent( eventTimestamp, XACTCalculator.CalculateAmplitudeRatio(min), XACTCalculator.CalculateAmplitudeRatio(max) ); } else if (eventType == 15) // ??? { // TODO: Codename OhGodNo -flibit } else if (eventType == 17) // Volume Repeat Event { // TODO: Codename OhGodNo -flibit } else { /* TODO: All XACT Events. * The following type information is based on * third-party contributions: * Type 9 - Marker Event * -flibit */ throw new NotImplementedException( "EVENT TYPE " + eventType.ToString() + " NOT IMPLEMENTED!" ); } } }