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 AudioEngine(string settingsFile) { if (String.IsNullOrEmpty(settingsFile)) { throw new ArgumentNullException("settingsFile"); } using (Stream stream = TitleContainer.OpenStream(settingsFile)) using (BinaryReader reader = new BinaryReader(stream)) { // Check the file header. Should be 'XGSF' if (reader.ReadUInt32() != 0x46534758) { throw new ArgumentException("XGSF format not recognized!"); } // Check the Content and Tool versions if (reader.ReadUInt16() != ContentVersion) { throw new ArgumentException("XGSF Content version!"); } if (reader.ReadUInt16() != 42) { throw new ArgumentException("XGSF Tool version!"); } // Unknown value reader.ReadUInt16(); // Last Modified, Unused reader.ReadUInt64(); // XACT Version, Unused reader.ReadByte(); // Number of AudioCategories ushort numCategories = reader.ReadUInt16(); // Number of XACT Variables ushort numVariables = reader.ReadUInt16(); // KEY#1 Length /*ushort numKeyOne =*/ reader.ReadUInt16(); // KEY#2 Length /*ushort numKeyTwo =*/ reader.ReadUInt16(); // Number of RPC Variables ushort numRPCs = reader.ReadUInt16(); // Number of DSP Presets/Parameters ushort numDSPPresets = reader.ReadUInt16(); ushort numDSPParameters = reader.ReadUInt16(); // Category Offset in XGS File uint categoryOffset = reader.ReadUInt32(); // Variable Offset in XGS File uint variableOffset = reader.ReadUInt32(); // KEY#1 Offset /*uint keyOneOffset =*/ reader.ReadUInt32(); // Category Name Index Offset, unused reader.ReadUInt32(); // KEY#2 Offset /*uint keyTwoOffset =*/ reader.ReadUInt32(); // Variable Name Index Offset, unused reader.ReadUInt32(); // Category Name Offset in XGS File uint categoryNameOffset = reader.ReadUInt32(); // Variable Name Offset in XGS File uint variableNameOffset = reader.ReadUInt32(); // RPC Variable Offset in XGS File uint rpcOffset = reader.ReadUInt32(); // DSP Preset/Parameter Offsets in XGS File uint dspPresetOffset = reader.ReadUInt32(); uint dspParameterOffset = reader.ReadUInt32(); /* Unknown table #1 * reader.BaseStream.Seek(keyOneOffset, SeekOrigin.Begin); * for (int i = 0; i < numKeyOne; i += 1) * { * // Appears to consistently be 16 shorts? * System.Console.WriteLine(reader.ReadInt16()); * } * /* OhGodNo * 1, -1, 4, -1, * 3, -1, -1, 7, * -1, 2, 5, -1, * 6, 0, -1, -1 * * Naddachance * 1, -1, 4, -1, * 5, -1, -1, -1, * -1, 2, -1, -1, * 3, 0, -1, -1 * * TFA * 1, -1, -1, -1, * -1, -1, -1, -1, * -1, 2, -1, -1, * -1, -0, -1, -1 */ /* Unknown table #2 * reader.BaseStream.Seek(keyTwoOffset, SeekOrigin.Begin); * for (int i = 0; i < numKeyTwo; i += 1) * { * // Appears to be between 16-20 shorts? * System.Console.WriteLine(reader.ReadInt16()); * } * /* OhGodNo * 2, 7, 1, -1, * -1, 10, 19, -1, * 11, 3, -1, -1, * 8, -1, 14, 5, * 12, 0, 4, 6 * * Naddachance * 2, 3, -1, -1, * 9, -1, 7, -1, * 10, 0, 1, 5, * -1, -1, -1, -1 * * TFA * 2, 3, -1, -1, * -1, -1, -1, -1, * -1, 0, 1, 5, * -1, -1, -1, -1 */ // Obtain the Audio Category Names reader.BaseStream.Seek(categoryNameOffset, SeekOrigin.Begin); string[] categoryNames = new string[numCategories]; for (int i = 0; i < numCategories; i += 1) { List <char> builtString = new List <char>(); while (reader.PeekChar() != 0) { builtString.Add(reader.ReadChar()); } reader.ReadChar(); // Null terminator categoryNames[i] = new string(builtString.ToArray()); } // Obtain the Audio Categories reader.BaseStream.Seek(categoryOffset, SeekOrigin.Begin); INTERNAL_categories = new List <AudioCategory>(); for (int i = 0; i < numCategories; i += 1) { // Maximum instances byte maxInstances = reader.ReadByte(); // Fade In/Out ushort fadeInMS = reader.ReadUInt16(); ushort fadeOutMS = reader.ReadUInt16(); // Instance Behavior Flags byte instanceFlags = reader.ReadByte(); int fadeType = instanceFlags & 0x07; int maxBehavior = instanceFlags >> 3; // Parent Category short parent = reader.ReadInt16(); // Volume float volume = XACTCalculator.CalculateAmplitudeRatio( XACTCalculator.ParseDecibel( reader.ReadByte() ) ); // Visibility Flags, unused reader.ReadByte(); // Add to the engine list and the parent category INTERNAL_categories.Add( new AudioCategory( categoryNames[i], volume, maxInstances, maxBehavior, fadeInMS, fadeOutMS, fadeType ) ); if (parent != -1) { INTERNAL_categories[parent].subCategories.Add( INTERNAL_categories[i] ); } } // Obtain the Variable Names reader.BaseStream.Seek(variableNameOffset, SeekOrigin.Begin); string[] variableNames = new string[numVariables]; for (int i = 0; i < numVariables; i += 1) { List <char> builtString = new List <char>(); while (reader.PeekChar() != 0) { builtString.Add(reader.ReadChar()); } reader.ReadChar(); // Null terminator variableNames[i] = new string(builtString.ToArray()); } // Obtain the Variables reader.BaseStream.Seek(variableOffset, SeekOrigin.Begin); INTERNAL_variables = new List <Variable>(); for (int i = 0; i < numVariables; i += 1) { // Variable Accessibility (See Variable constructor) byte varFlags = reader.ReadByte(); // Variable Value, Boundaries float initialValue = reader.ReadSingle(); float minValue = reader.ReadSingle(); float maxValue = reader.ReadSingle(); // Add to the engine list INTERNAL_variables.Add( new Variable( variableNames[i], (varFlags & 0x01) != 0, (varFlags & 0x02) != 0, (varFlags & 0x04) == 0, (varFlags & 0x08) != 0, initialValue, minValue, maxValue ) ); } // Obtain the RPC Curves reader.BaseStream.Seek(rpcOffset, SeekOrigin.Begin); INTERNAL_RPCs = new Dictionary <long, RPC>(); for (int i = 0; i < numRPCs; i += 1) { // RPC "Code", used by the SoundBanks long rpcCode = reader.BaseStream.Position; // RPC Variable ushort rpcVariable = reader.ReadUInt16(); // Number of RPC Curve Points byte numPoints = reader.ReadByte(); // RPC Parameter ushort rpcParameter = reader.ReadUInt16(); // RPC Curve Points RPCPoint[] rpcPoints = new RPCPoint[numPoints]; for (byte j = 0; j < numPoints; j += 1) { float x = reader.ReadSingle(); float y = reader.ReadSingle(); byte type = reader.ReadByte(); rpcPoints[j] = new RPCPoint( x, y, (RPCPointType)type ); } // Add to the engine list INTERNAL_RPCs.Add( rpcCode, new RPC( INTERNAL_variables[rpcVariable].Name, rpcParameter, rpcPoints ) ); } // Obtain the DSP Parameters reader.BaseStream.Seek(dspParameterOffset, SeekOrigin.Begin); INTERNAL_dspParameters = new List <DSPParameter>(); for (int i = 0; i < numDSPParameters; i += 1) { // Effect Parameter Type byte type = reader.ReadByte(); // Effect value, boundaries float value = reader.ReadSingle(); float minVal = reader.ReadSingle(); float maxVal = reader.ReadSingle(); // Unknown value reader.ReadUInt16(); // Add to Parameter list INTERNAL_dspParameters.Add( new DSPParameter( type, value, minVal, maxVal ) ); } // Obtain the DSP Presets reader.BaseStream.Seek(dspPresetOffset, SeekOrigin.Begin); INTERNAL_dspPresets = new Dictionary <long, DSPPreset>(); int total = 0; for (int i = 0; i < numDSPPresets; i += 1) { // DSP "Code", used by the SoundBanks long dspCode = reader.BaseStream.Position; // Preset Accessibility bool global = (reader.ReadByte() == 1); // Number of preset parameters uint numParams = reader.ReadUInt32(); // Obtain DSP Parameters DSPParameter[] parameters = new DSPParameter[numParams]; for (uint j = 0; j < numParams; j += 1) { parameters[j] = INTERNAL_dspParameters[total]; total += 1; } // Add to DSP Preset list INTERNAL_dspPresets.Add( dspCode, new DSPPreset( global, parameters ) ); } } // Create the WaveBank Dictionary INTERNAL_waveBanks = new Dictionary <string, WaveBank>(); // Finally. IsDisposed = false; }
public XACTSound(BinaryReader reader) { // Sound Effect Flags byte soundFlags = reader.ReadByte(); bool complex = (soundFlags & 0x01) != 0; // AudioCategory Index Category = reader.ReadUInt16(); // Sound Volume Volume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Sound Pitch Pitch = (reader.ReadInt16() / 1000.0f); // Unknown value reader.ReadByte(); // Length of Sound Entry, unused reader.ReadUInt16(); // Number of Sound Clips if (complex) { INTERNAL_clips = new XACTClip[reader.ReadByte()]; } else { // Simple Sounds always have 1 PlayWaveEvent. INTERNAL_clips = new XACTClip[1]; ushort track = reader.ReadUInt16(); byte waveBank = reader.ReadByte(); INTERNAL_clips[0] = new XACTClip(track, waveBank); } // Parse RPC Properties List <uint> rpcCodeList = new List <uint>(); if ((soundFlags & 0x0E) != 0) { // RPC data length ushort rpcDataLength = reader.ReadUInt16(); ushort totalDataRead = 2; /* For some reason XACT can have separate sets of codes. * I dunno why, but I bet we should be separating them too. * -flibit */ while (totalDataRead < rpcDataLength) { // Number of RPC Presets (in this block) byte numCodes = reader.ReadByte(); // Obtain RPC curve codes (in this block) for (byte i = 0; i < numCodes; i += 1) { rpcCodeList.Add(reader.ReadUInt32()); } totalDataRead += (ushort)(1 + (4 * numCodes)); } } RPCCodes = rpcCodeList.ToArray(); // Array may be empty! It's okay! // Parse DSP Presets DSPCodes = new uint[0]; // Eww... -flibit if ((soundFlags & 0x10) != 0) { // DSP Presets Length, unused reader.ReadUInt16(); // Number of DSP Presets DSPCodes = new uint[reader.ReadByte()]; // Obtain DSP Preset codes for (byte j = 0; j < DSPCodes.Length; j += 1) { DSPCodes[j] = reader.ReadUInt32(); } } // Parse Sound Events if (complex) { for (int i = 0; i < INTERNAL_clips.Length; i += 1) { // XACT Clip volume double clipVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // XACT Clip Offset in Bank uint offset = reader.ReadUInt32(); // Unknown value reader.ReadUInt32(); // Store this for when we're done reading the clip. long curPos = reader.BaseStream.Position; // Go to the Clip in the Bank. reader.BaseStream.Seek(offset, SeekOrigin.Begin); // Parse the Clip. INTERNAL_clips[i] = new XACTClip(reader, clipVolume); // Back to where we were... reader.BaseStream.Seek(curPos, SeekOrigin.Begin); } } HasLoadedTracks = false; }
public XACTSound(BinaryReader reader) { // Sound Effect Flags byte soundFlags = reader.ReadByte(); bool complex = (soundFlags & 0x01) != 0; // AudioCategory Index Category = reader.ReadUInt16(); // Sound Volume Volume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Sound Pitch Pitch = (reader.ReadInt16() / 1000.0f); // Unknown value reader.ReadByte(); // Length of Sound Entry, unused reader.ReadUInt16(); // Number of Sound Clips if (complex) { INTERNAL_clips = new XACTClip[reader.ReadByte()]; } else { // Simple Sounds always have 1 PlayWaveEvent. INTERNAL_clips = new XACTClip[1]; ushort track = reader.ReadUInt16(); byte waveBank = reader.ReadByte(); INTERNAL_clips[0] = new XACTClip(track, waveBank); } // Parse RPC Properties RPCCodes = new List <uint[]>(); if ((soundFlags & 0x0E) != 0) { // RPC data length ushort rpcDataLength = reader.ReadUInt16(); ushort totalDataRead = 2; while (totalDataRead < rpcDataLength) { // Number of RPC Presets (for this track) uint[] codeList = new uint[reader.ReadByte()]; // Obtain RPC curve codes (in this block) for (int i = 0; i < codeList.Length; i += 1) { codeList[i] = reader.ReadUInt32(); } // Add this track's code list to the master list RPCCodes.Add(codeList); totalDataRead += (ushort)(1 + (4 * codeList.Length)); } } // Parse DSP Presets DSPCodes = new uint[0]; // Eww... -flibit if ((soundFlags & 0x10) != 0) { // DSP Presets Length, unused reader.ReadUInt16(); // Number of DSP Presets DSPCodes = new uint[reader.ReadByte()]; // Obtain DSP Preset codes for (byte j = 0; j < DSPCodes.Length; j += 1) { DSPCodes[j] = reader.ReadUInt32(); } } // Parse Sound Events if (complex) { for (int i = 0; i < INTERNAL_clips.Length; i += 1) { // XACT Clip volume double clipVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // XACT Clip Offset in Bank uint offset = reader.ReadUInt32(); // XACT Clip filter byte filterFlags = reader.ReadByte(); byte filterType; if ((filterFlags & 0x01) == 0x01) { filterType = (byte)((filterFlags >> 1) & 0x02); } else { filterType = 0xFF; } reader.ReadByte(); // QFactor? reader.ReadUInt16(); // Frequency // Store this for when we're done reading the clip. long curPos = reader.BaseStream.Position; // Go to the Clip in the Bank. reader.BaseStream.Seek(offset, SeekOrigin.Begin); // Parse the Clip. INTERNAL_clips[i] = new XACTClip(reader, clipVolume, filterType); // Back to where we were... reader.BaseStream.Seek(curPos, SeekOrigin.Begin); } } HasLoadedTracks = false; }
public XACTSound(BinaryReader reader) { // Sound Effect Flags byte soundFlags = reader.ReadByte(); bool complex = (soundFlags & 0x01) != 0; // AudioCategory Index Category = reader.ReadUInt16(); // Sound Volume Volume = XACTCalculator.ParseDecibel(reader.ReadByte()); // Sound Pitch Pitch = (reader.ReadInt16() / 1000.0f); // Unknown value reader.ReadByte(); // Length of Sound Entry, unused reader.ReadUInt16(); // Number of Sound Clips if (complex) { INTERNAL_clips = new XACTClip[reader.ReadByte()]; } else { // Simple Sounds always have 1 PlayWaveEvent. INTERNAL_clips = new XACTClip[1]; ushort track = reader.ReadUInt16(); byte waveBank = reader.ReadByte(); INTERNAL_clips[0] = new XACTClip(track, waveBank); } // Parse RPC Properties RPCCodes = new uint[0]; // Eww... -flibit if ((soundFlags & 0x0E) != 0) { // RPC data length, unused ushort rpcDataLength = reader.ReadUInt16(); // Number of RPC Presets RPCCodes = new uint[reader.ReadByte()]; // Obtain RPC curve codes for (byte i = 0; i < RPCCodes.Length; i += 1) { RPCCodes[i] = reader.ReadUInt32(); } // Seek past the rest - we dunno what it is yet. reader.BaseStream.Seek( rpcDataLength - 2 - 1 - (4 * RPCCodes.Length), SeekOrigin.Current ); } // Parse DSP Presets DSPCodes = new uint[0]; // Eww... -flibit if ((soundFlags & 0x10) != 0) { // DSP Presets Length, unused reader.ReadUInt16(); // Number of DSP Presets DSPCodes = new uint[reader.ReadByte()]; // Obtain DSP Preset codes for (byte j = 0; j < DSPCodes.Length; j += 1) { DSPCodes[j] = reader.ReadUInt32(); } } // Parse Sound Events if (complex) { for (int i = 0; i < INTERNAL_clips.Length; i += 1) { // XACT Clip volume double clipVolume = XACTCalculator.ParseDecibel(reader.ReadByte()); // XACT Clip Offset in Bank uint offset = reader.ReadUInt32(); // Unknown value reader.ReadUInt32(); // Store this for when we're done reading the clip. long curPos = reader.BaseStream.Position; // Go to the Clip in the Bank. reader.BaseStream.Seek(offset, SeekOrigin.Begin); // Parse the Clip. INTERNAL_clips[i] = new XACTClip(reader, clipVolume); // Back to where we were... reader.BaseStream.Seek(curPos, SeekOrigin.Begin); } } HasLoadedTracks = false; }
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!" ); } } }
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!" ); } } }