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 lookAheadTime of 250 milliseconds or greater.</remarks>
        public AudioEngine(string settingsFile, TimeSpan lookAheadTime, string rendererId)
        {
            if (string.IsNullOrEmpty(settingsFile))
            {
                throw new ArgumentNullException("settingsFile");
            }

            // Read the xact settings file
            // Credits to alisci01 for initial format documentation
            using (var stream = OpenStream(settingsFile))
                using (var reader = new BinaryReader(stream))
                {
                    uint magic = reader.ReadUInt32();
                    if (magic != 0x46534758) //'XGFS'
                    {
                        throw new ArgumentException("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();
                        v.Name      = varNames[i];
                        v.Flags     = reader.ReadByte();
                        v.InitValue = reader.ReadSingle();
                        v.MinValue  = reader.ReadSingle();
                        v.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();
                            curve.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();
        }