internal RpcVariable[] CreateCueVariables() { var clone = new RpcVariable[_cueVariables.Length]; Array.Copy(_cueVariables, clone, _cueVariables.Length); return(clone); }
/// <param name="settingsFile">Path to a XACT settings file.</param> /// <param name="lookAheadTime"> /// Determines how many milliseconds the engine will look ahead when /// determing when to transition to another sound. /// </param> /// <param name="rendererId">A string that specifies the audio renderer to use.</param> /// <remarks> /// For the best results, use a <paramref name="lookAheadTime"/> of 250 milliseconds or greater. /// </remarks> public AudioEngine(string settingsFile, TimeSpan lookAheadTime, string rendererId) { if (string.IsNullOrEmpty(settingsFile)) { throw new ArgumentNullException(nameof(settingsFile)); } // Read the xact settings file // Credits to alisci01 for initial format documentation using (var reader = new BinaryReader(OpenStream(settingsFile))) { uint magic = reader.ReadUInt32(); if (magic != 0x46534758) //'XGFS' { throw new InvalidDataException("XGS format not recognized"); } reader.ReadUInt16(); // toolVersion uint formatVersion = reader.ReadUInt16(); if (formatVersion != 42) { Debug.WriteLine("Warning: XGS format " + formatVersion + " not supported!"); } reader.ReadUInt16(); // crc reader.ReadUInt32(); // lastModifiedLow reader.ReadUInt32(); // lastModifiedHigh reader.ReadByte(); //unkn, 0x03. Platform? uint numCats = reader.ReadUInt16(); uint numVars = reader.ReadUInt16(); reader.ReadUInt16(); //unkn, 0x16 reader.ReadUInt16(); //unkn, 0x16 uint numRpc = reader.ReadUInt16(); uint numDspPresets = reader.ReadUInt16(); uint numDspParams = reader.ReadUInt16(); uint catsOffset = reader.ReadUInt32(); uint varsOffset = reader.ReadUInt32(); reader.ReadUInt32(); //unknown, leads to a short with value of 1? reader.ReadUInt32(); // catNameIndexOffset reader.ReadUInt32(); //unknown, two shorts of values 2 and 3? reader.ReadUInt32(); // varNameIndexOffset uint catNamesOffset = reader.ReadUInt32(); uint varNamesOffset = reader.ReadUInt32(); uint rpcOffset = reader.ReadUInt32(); reader.ReadUInt32(); // dspPresetsOffset uint dspParamsOffset = reader.ReadUInt32(); reader.BaseStream.Seek(catNamesOffset, SeekOrigin.Begin); string[] categoryNames = ReadNullTerminatedStrings(numCats, reader); Categories = new AudioCategory[numCats]; reader.BaseStream.Seek(catsOffset, SeekOrigin.Begin); for (int i = 0; i < numCats; i++) { Categories[i] = new AudioCategory(this, categoryNames[i], reader); _categoryLookup.Add(categoryNames[i], i); } reader.BaseStream.Seek(varNamesOffset, SeekOrigin.Begin); string[] varNames = ReadNullTerminatedStrings(numVars, reader); var variables = new List <RpcVariable>(); var cueVariables = new List <RpcVariable>(); var globalVariables = new List <RpcVariable>(); reader.BaseStream.Seek(varsOffset, SeekOrigin.Begin); for (var i = 0; i < numVars; i++) { var v = new RpcVariable { Name = varNames[i], Flags = reader.ReadByte(), InitValue = reader.ReadSingle(), MinValue = reader.ReadSingle(), MaxValue = reader.ReadSingle() }; v.Value = v.InitValue; variables.Add(v); if (!v.IsGlobal) { cueVariables.Add(v); } else { globalVariables.Add(v); _variableLookup.Add(v.Name, globalVariables.Count - 1); } } _cueVariables = cueVariables.ToArray(); _variables = globalVariables.ToArray(); var reverbCurves = new List <RpcCurve>(); RpcCurves = new RpcCurve[numRpc]; if (numRpc > 0) { reader.BaseStream.Seek(rpcOffset, SeekOrigin.Begin); for (var i = 0; i < numRpc; i++) { var curve = new RpcCurve { FileOffset = (uint)reader.BaseStream.Position }; var variable = variables[reader.ReadUInt16()]; if (variable.IsGlobal) { curve.IsGlobal = true; curve.Variable = globalVariables.FindIndex(e => e.Name == variable.Name); } else { curve.IsGlobal = false; curve.Variable = cueVariables.FindIndex(e => e.Name == variable.Name); } var pointCount = (int)reader.ReadByte(); curve.Parameter = (RpcParameter)reader.ReadUInt16(); curve.Points = new RpcPoint[pointCount]; for (var j = 0; j < pointCount; j++) { curve.Points[j].Position = reader.ReadSingle(); curve.Points[j].Value = reader.ReadSingle(); curve.Points[j].Type = (RpcPointType)reader.ReadByte(); } // If the parameter is greater than the max then this is a DSP // parameter which is for reverb. var dspParameter = curve.Parameter - RpcParameter.NumParameters; if (dspParameter >= 0 && variable.IsGlobal) { reverbCurves.Add(curve); } RpcCurves[i] = curve; } } _reverbCurves = reverbCurves.ToArray(); if (numDspPresets > 0) { // Note: It seemed like MS designed this to support multiple // DSP effects, but in practice XACT only has one... Microsoft Reverb. // // So because of this we know exactly how many presets and // parameters we should have. if (numDspPresets != 1) { throw new Exception("Unexpected number of DSP presets!"); } if (numDspParams != 22) { throw new Exception("Unexpected number of DSP parameters!"); } reader.BaseStream.Seek(dspParamsOffset, SeekOrigin.Begin); _reverbSettings = new ReverbSettings(reader); } } _stopwatch = new Stopwatch(); _stopwatch.Start(); }