コード例 #1
0
        public XactSound(AudioEngine engine, SoundBank soundBank, BinaryReader soundReader)
        {
            _soundBank = soundBank;

            var flags = soundReader.ReadByte();

            _complexSound = (flags & 0x1) != 0;
            var hasRPCs = (flags & 0x0E) != 0;
            var hasDSPs = (flags & 0x10) != 0;

            _categoryId = soundReader.ReadUInt16();
            _volume     = XactHelpers.ParseVolumeFromDecibels(soundReader.ReadByte());
            _pitch      = soundReader.ReadInt16() / 1000f;
            soundReader.ReadByte();   //priority
            soundReader.ReadUInt16(); // filter stuff?

            int numClips = 0;

            if (_complexSound)
            {
                numClips = soundReader.ReadByte();
            }
            else
            {
                _trackIndex    = soundReader.ReadUInt16();
                _waveBankIndex = soundReader.ReadByte();
            }

            if (!hasRPCs)
            {
                RpcCurves = Array.Empty <int>();
            }
            else
            {
                var current = soundReader.BaseStream.Position;

                // This doesn't seem to be used... might have been there
                // to allow for some future file format expansion.
                var dataLength = soundReader.ReadUInt16();

                var numPresets = soundReader.ReadByte();
                RpcCurves = new int[numPresets];
                for (var i = 0; i < numPresets; i++)
                {
                    RpcCurves[i] = engine.GetRpcIndex(soundReader.ReadUInt32());
                }

                // Just in case seek to the right spot.
                soundReader.BaseStream.Seek(current + dataLength, SeekOrigin.Begin);
            }

            if (!hasDSPs)
            {
                _useReverb = false;
            }
            else
            {
                // The file format for this seems to follow the pattern for
                // the RPC curves above, but in this case XACT only supports
                // a single effect...  Microsoft Reverb... so just set it.
                _useReverb = true;
                soundReader.BaseStream.Seek(7, SeekOrigin.Current);
            }

            if (_complexSound)
            {
                _soundClips = new XactClip[numClips];
                for (int i = 0; i < numClips; i++)
                {
                    _soundClips[i] = new XactClip(soundBank, soundReader, _useReverb);
                }
            }

            var category = engine.Categories[_categoryId];

            category.AddSound(this);
        }
コード例 #2
0
        private WaveBank(
            AudioEngine audioEngine, string waveBankFilename, bool streaming, int offset, int packetsize)
        {
            if (audioEngine == null)
            {
                throw new ArgumentNullException(nameof(audioEngine));
            }

            if (string.IsNullOrEmpty(waveBankFilename))
            {
                throw new ArgumentNullException(nameof(waveBankFilename));
            }

            // Is this a streaming wavebank?
            if (streaming)
            {
                if (offset != 0)
                {
                    throw new ArgumentException(
                              "We only support a zero offset in streaming banks.", nameof(offset));
                }

                if (packetsize < 2)
                {
                    throw new ArgumentException(
                              "The packet size must be greater than 2.", nameof(packetsize));
                }

                _streaming  = true;
                _offset     = offset;
                _packetSize = packetsize;
            }

            //XWB PARSING
            //Adapted from MonoXNA
            //Originally adaped from Luigi Auriemma's unxwb

            WaveBankHeader wavebankheader;
            WaveBankData   wavebankdata;

            wavebankdata.EntryNameElementSize = 0;
            wavebankdata.CompactFormat        = 0;
            wavebankdata.Alignment            = 0;
            wavebankdata.BuildTime            = 0;

            int wavebank_offset = 0;

            _waveBankFileName = waveBankFilename;

            using (var reader = new BinaryReader(AudioEngine.OpenStream(waveBankFilename)))
            {
                reader.ReadBytes(4);

                _version = wavebankheader.Version = reader.ReadInt32();

                int last_segment = 4;
                //if (wavebankheader.Version == 1) goto WAVEBANKDATA;
                if (wavebankheader.Version <= 3)
                {
                    last_segment = 3;
                }
                if (wavebankheader.Version >= 42)
                {
                    reader.ReadInt32();                                  // skip HeaderVersion
                }
                wavebankheader.Segments = new Segment[5];

                for (int i = 0; i <= last_segment; i++)
                {
                    wavebankheader.Segments[i].Offset = reader.ReadInt32();
                    wavebankheader.Segments[i].Length = reader.ReadInt32();
                }

                reader.BaseStream.Seek(wavebankheader.Segments[0].Offset, SeekOrigin.Begin);

                //WAVEBANKDATA:

                wavebankdata.Flags      = reader.ReadInt32();
                wavebankdata.EntryCount = reader.ReadInt32();

                if ((wavebankheader.Version == 2) || (wavebankheader.Version == 3))
                {
                    wavebankdata.BankName = System.Text.Encoding.UTF8.GetString(
                        reader.ReadBytes(16), 0, 16).Replace("\0", "", StringComparison.InvariantCulture);
                }
                else
                {
                    wavebankdata.BankName = System.Text.Encoding.UTF8.GetString(
                        reader.ReadBytes(64), 0, 64).Replace("\0", "", StringComparison.InvariantCulture);
                }

                _bankName = wavebankdata.BankName;

                if (wavebankheader.Version == 1)
                {
                    //wavebank_offset = (int)ftell(fd) - file_offset;
                    wavebankdata.EntryMetaDataElementSize = 20;
                }
                else
                {
                    wavebankdata.EntryMetaDataElementSize = reader.ReadInt32();
                    wavebankdata.EntryNameElementSize     = reader.ReadInt32();
                    wavebankdata.Alignment = reader.ReadInt32();
                    wavebank_offset        = wavebankheader.Segments[1].Offset; //METADATASEGMENT
                }

                if ((wavebankdata.Flags & Flag_Compact) != 0)
                {
                    reader.ReadInt32(); // compact_format
                }

                _playRegionOffset = wavebankheader.Segments[last_segment].Offset;
                if (_playRegionOffset == 0)
                {
                    _playRegionOffset =
                        wavebank_offset +
                        (wavebankdata.EntryCount * wavebankdata.EntryMetaDataElementSize);
                }

                int segidx_entry_name = 2;
                if (wavebankheader.Version >= 42)
                {
                    segidx_entry_name = 3;
                }

                if ((wavebankheader.Segments[segidx_entry_name].Offset != 0) &&
                    (wavebankheader.Segments[segidx_entry_name].Length != 0))
                {
                    if (wavebankdata.EntryNameElementSize == -1)
                    {
                        wavebankdata.EntryNameElementSize = 0;
                    }
                    byte[] entry_name = new byte[wavebankdata.EntryNameElementSize + 1];
                    entry_name[wavebankdata.EntryNameElementSize] = 0;
                }

                _sounds  = new SoundEffect[wavebankdata.EntryCount];
                _streams = new StreamInfo[wavebankdata.EntryCount];

                reader.BaseStream.Seek(wavebank_offset, SeekOrigin.Begin);

                // The compact format requires us to load stuff differently.
                var isCompactFormat = (wavebankdata.Flags & Flag_Compact) != 0;
                if (isCompactFormat)
                {
                    // Load the sound data offset table from disk.
                    for (var i = 0; i < wavebankdata.EntryCount; i++)
                    {
                        var len = reader.ReadInt32();
                        _streams[i].Format     = wavebankdata.CompactFormat;
                        _streams[i].FileOffset = (len & ((1 << 21) - 1)) * wavebankdata.Alignment;
                    }

                    // Now figure out the sound data lengths.
                    for (var i = 0; i < wavebankdata.EntryCount; i++)
                    {
                        int nextOffset;
                        if (i == (wavebankdata.EntryCount - 1))
                        {
                            nextOffset = wavebankheader.Segments[last_segment].Length;
                        }
                        else
                        {
                            nextOffset = _streams[i + 1].FileOffset;
                        }

                        // The next and current offsets used to calculate the length.
                        _streams[i].FileLength = nextOffset - _streams[i].FileOffset;
                    }
                }
                else
                {
                    for (var i = 0; i < wavebankdata.EntryCount; i++)
                    {
                        var info = new StreamInfo();
                        if (wavebankheader.Version == 1)
                        {
                            info.Format     = reader.ReadInt32();
                            info.FileOffset = reader.ReadInt32();
                            info.FileLength = reader.ReadInt32();
                            info.LoopStart  = reader.ReadInt32();
                            info.LoopLength = reader.ReadInt32();
                        }
                        else
                        {
                            var flagsAndDuration = reader.ReadInt32(); // Unused

                            if (wavebankdata.EntryMetaDataElementSize >= 8)
                            {
                                info.Format = reader.ReadInt32();
                            }
                            if (wavebankdata.EntryMetaDataElementSize >= 12)
                            {
                                info.FileOffset = reader.ReadInt32();
                            }
                            if (wavebankdata.EntryMetaDataElementSize >= 16)
                            {
                                info.FileLength = reader.ReadInt32();
                            }
                            if (wavebankdata.EntryMetaDataElementSize >= 20)
                            {
                                info.LoopStart = reader.ReadInt32();
                            }
                            if (wavebankdata.EntryMetaDataElementSize >= 24)
                            {
                                info.LoopLength = reader.ReadInt32();
                            }
                        }

                        // TODO: What is this doing?
                        if (wavebankdata.EntryMetaDataElementSize < 24)
                        {
                            if (info.FileLength != 0)
                            {
                                info.FileLength = wavebankheader.Segments[last_segment].Length;
                            }
                        }

                        _streams[i] = info;
                    }
                }

                // If this isn't a streaming wavebank then load all the sounds now.
                if (!_streaming)
                {
                    for (var i = 0; i < _streams.Length; i++)
                    {
                        var info = _streams[i];

                        // Read the data.
                        reader.BaseStream.Seek(info.FileOffset + _playRegionOffset, SeekOrigin.Begin);

                        // TODO: pool this memory
                        var audiodata = reader.ReadBytes(info.FileLength);
                        DecodeFormat(info.Format, out MiniFormatTag codec, out int channels, out int rate, out int alignment);

                        // Call the special constuctor on SoundEffect to sort it out.
                        _sounds[i] = new SoundEffect(
                            audiodata, codec, channels, rate, alignment, info.LoopStart, info.LoopLength);
                    }

                    _streams = null;
                }
            }

            audioEngine.Wavebanks[_bankName] = this;
            IsPrepared = true;
        }
コード例 #3
0
 /// <param name="audioEngine">Instance of the AudioEngine to associate this wave bank with.</param>
 /// <param name="streamingWaveBankFilename">Path to the .xwb to stream from.</param>
 /// <param name="offset">DVD sector-aligned offset within the wave bank data file.</param>
 /// <param name="packetsize">Stream packet size, in sectors, to use for each stream. The minimum value is 2.</param>
 /// <remarks>
 /// <para>This constructor streams wave data as needed.</para>
 /// <para>Note that packetsize is in sectors, which is 2048 bytes.</para>
 /// <para>AudioEngine.Update() must be called at least once before using data from a streaming wave bank.</para>
 /// </remarks>
 public WaveBank(AudioEngine audioEngine, string streamingWaveBankFilename, int offset, short packetsize)
     : this(audioEngine, streamingWaveBankFilename, true, offset, packetsize)
 {
 }
コード例 #4
0
 /// <param name="audioEngine">Instance of the AudioEngine to associate this wave bank with.</param>
 /// <param name="nonStreamingWaveBankFilename">Path to the .xwb file to load.</param>
 /// <remarks>This constructor immediately loads all wave data into memory at once.</remarks>
 public WaveBank(AudioEngine audioEngine, string nonStreamingWaveBankFilename)
     : this(audioEngine, nonStreamingWaveBankFilename, false, 0, 0)
 {
 }
コード例 #5
0
        /// <param name="audioEngine">The engine that will be associated with this sound bank.</param>
        /// <param name="fileName">Path to a .xsb sound bank file.</param>
        public SoundBank(AudioEngine audioEngine, string fileName)
        {
            _audioengine = audioEngine ?? throw new ArgumentNullException(nameof(audioEngine));

            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException(nameof(fileName));
            }

            using (var stream = AudioEngine.OpenStream(fileName))
                using (var reader = new BinaryReader(stream))
                {
                    // Thanks to Liandril for "xactxtract" for some of the offsets.

                    uint magic = reader.ReadUInt32();
                    if (magic != 0x4B424453) //"SDBK"
                    {
                        throw new Exception("Bad soundbank format");
                    }

                    reader.ReadUInt16(); // toolVersion

                    uint formatVersion = reader.ReadUInt16();
                    if (formatVersion != 43)
                    {
                        Debug.WriteLine("Warning: SoundBank format {0} not supported.", formatVersion);
                    }

                    reader.ReadUInt16(); // crc, TODO: Verify crc (FCS16)

                    reader.ReadUInt32(); // lastModifiedLow
                    reader.ReadUInt32(); // lastModifiedHigh
                    reader.ReadByte();   // platform ???

                    uint numSimpleCues  = reader.ReadUInt16();
                    uint numComplexCues = reader.ReadUInt16();
                    reader.ReadUInt16(); //unkn
                    reader.ReadUInt16(); // numTotalCues
                    uint numWaveBanks = reader.ReadByte();
                    reader.ReadUInt16(); // numSounds
                    uint cueNameTableLen = reader.ReadUInt16();
                    reader.ReadUInt16(); //unkn

                    uint simpleCuesOffset  = reader.ReadUInt32();
                    uint complexCuesOffset = reader.ReadUInt32(); //unkn
                    uint cueNamesOffset    = reader.ReadUInt32();
                    reader.ReadUInt32();                          //unkn
                    reader.ReadUInt32();                          // variationTablesOffset
                    reader.ReadUInt32();                          //unkn
                    uint waveBankNameTableOffset = reader.ReadUInt32();
                    reader.ReadUInt32();                          // cueNameHashTableOffset
                    reader.ReadUInt32();                          // cueNameHashValsOffset
                    reader.ReadUInt32();                          // soundsOffset

                    //parse wave bank name table
                    stream.Seek(waveBankNameTableOffset, SeekOrigin.Begin);
                    _waveBanks     = new WaveBank[numWaveBanks];
                    _waveBankNames = new string[numWaveBanks];
                    for (int i = 0; i < numWaveBanks; i++)
                    {
                        _waveBankNames[i] = System.Text.Encoding.UTF8
                                            .GetString(reader.ReadBytes(64)).Replace("\0", "", StringComparison.Ordinal);
                    }

                    //parse cue name table
                    stream.Seek(cueNamesOffset, SeekOrigin.Begin);
                    string[] cueNames = System.Text.Encoding.UTF8
                                        .GetString(reader.ReadBytes((int)cueNameTableLen)).Split('\0');

                    // Simple cues
                    if (numSimpleCues > 0)
                    {
                        stream.Seek(simpleCuesOffset, SeekOrigin.Begin);
                        for (int i = 0; i < numSimpleCues; i++)
                        {
                            reader.ReadByte(); // flags
                            uint soundOffset = reader.ReadUInt32();

                            var oldPosition = stream.Position;
                            stream.Seek(soundOffset, SeekOrigin.Begin);
                            var sound = new XactSound(audioEngine, this, reader);
                            stream.Seek(oldPosition, SeekOrigin.Begin);

                            _entries.Add(
                                cueNames[i],
                                new XactCueEntry(new XactSound[] { sound }, _defaultProbability));
                        }
                    }

                    // Complex cues
                    if (numComplexCues > 0)
                    {
                        stream.Seek(complexCuesOffset, SeekOrigin.Begin);
                        for (int i = 0; i < numComplexCues; i++)
                        {
                            byte flags = reader.ReadByte();
                            if (((flags >> 2) & 1) != 0)
                            {
                                uint soundOffset = reader.ReadUInt32();
                                reader.ReadUInt32(); //unkn

                                var oldPosition = stream.Position;
                                stream.Seek(soundOffset, SeekOrigin.Begin);
                                var sound = new XactSound(audioEngine, this, reader);
                                stream.Seek(oldPosition, SeekOrigin.Begin);

                                _entries.Add(
                                    cueNames[numSimpleCues + i],
                                    new XactCueEntry(new XactSound[] { sound }, _defaultProbability));
                            }
                            else
                            {
                                uint variationTableOffset = reader.ReadUInt32();
                                reader.ReadUInt32(); // transitionTableOffset

                                //parse variation table
                                long savepos = stream.Position;
                                stream.Seek(variationTableOffset, SeekOrigin.Begin);

                                uint numEntries     = reader.ReadUInt16();
                                uint variationflags = reader.ReadUInt16();
                                reader.ReadByte();
                                reader.ReadUInt16();
                                reader.ReadByte();

                                var cueSounds = new XactSound[numEntries];
                                var probs     = new float[numEntries];

                                uint tableType = (variationflags >> 3) & 0x7;
                                for (int j = 0; j < numEntries; j++)
                                {
                                    switch (tableType)
                                    {
                                    case 0: //Wave
                                    {
                                        int trackIndex    = reader.ReadUInt16();
                                        int waveBankIndex = reader.ReadByte();
                                        reader.ReadByte(); // weightMin
                                        reader.ReadByte(); // weightMax

                                        cueSounds[j] = new XactSound(this, waveBankIndex, trackIndex);
                                        break;
                                    }

                                    case 1:
                                    {
                                        uint soundOffset = reader.ReadUInt32();
                                        reader.ReadByte(); // weightMin
                                        reader.ReadByte(); // weightMax

                                        var oldPosition = stream.Position;
                                        stream.Seek(soundOffset, SeekOrigin.Begin);
                                        cueSounds[j] = new XactSound(audioEngine, this, reader);
                                        stream.Seek(oldPosition, SeekOrigin.Begin);
                                        break;
                                    }

                                    case 3:
                                    {
                                        uint soundOffset = reader.ReadUInt32();
                                        reader.ReadSingle(); // weightMin
                                        reader.ReadSingle(); // weightMax
                                        reader.ReadUInt32(); // flags

                                        var oldPosition = stream.Position;
                                        stream.Seek(soundOffset, SeekOrigin.Begin);
                                        cueSounds[j] = new XactSound(audioEngine, this, reader);
                                        stream.Seek(oldPosition, SeekOrigin.Begin);
                                        break;
                                    }

                                    case 4: //CompactWave
                                    {
                                        int trackIndex    = reader.ReadUInt16();
                                        int waveBankIndex = reader.ReadByte();
                                        cueSounds[j] = new XactSound(this, waveBankIndex, trackIndex);
                                        break;
                                    }

                                    default:
                                        throw new NotSupportedException();
                                    }
                                }

                                stream.Seek(savepos, SeekOrigin.Begin);

                                _entries.Add(
                                    cueNames[numSimpleCues + i], new XactCueEntry(cueSounds, probs));
                            }

                            // Instance limiting
                            reader.ReadByte();   //instanceLimit
                            reader.ReadUInt16(); //fadeInSec, divide by 1000f
                            reader.ReadUInt16(); //fadeOutSec, divide by 1000f
                            reader.ReadByte();   //instanceFlags
                        }
                    }
                }
        }