/// <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) { if (audioEngine == null) { throw new ArgumentNullException("audioEngine"); } if (string.IsNullOrEmpty(nonStreamingWaveBankFilename)) { throw new ArgumentNullException("nonStreamingWaveBankFilename"); } //XWB PARSING //Adapted from MonoXNA //Originally adaped from Luigi Auriemma's unxwb WaveBankHeader wavebankheader; WaveBankData wavebankdata; WaveBankEntry wavebankentry; wavebankdata.EntryNameElementSize = 0; wavebankdata.CompactFormat = 0; wavebankdata.Alignment = 0; wavebankdata.BuildTime = 0; wavebankentry.Format = 0; wavebankentry.PlayRegion.Length = 0; wavebankentry.PlayRegion.Offset = 0; int wavebank_offset = 0; BinaryReader reader = new BinaryReader(AudioEngine.OpenStream(nonStreamingWaveBankFilename)); reader.ReadBytes(4); 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", ""); } else { wavebankdata.BankName = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(64), 0, 64).Replace("\0", ""); } _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 } int playregion_offset = wavebankheader.Segments[last_segment].Offset; if (playregion_offset == 0) { playregion_offset = 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]; for (int current_entry = 0; current_entry < wavebankdata.EntryCount; current_entry++) { reader.BaseStream.Seek(wavebank_offset, SeekOrigin.Begin); //SHOWFILEOFF; //memset(&wavebankentry, 0, sizeof(wavebankentry)); wavebankentry.LoopRegion.Length = 0; wavebankentry.LoopRegion.Offset = 0; if ((wavebankdata.Flags & Flag_Compact) != 0) { int len = reader.ReadInt32(); wavebankentry.Format = wavebankdata.CompactFormat; wavebankentry.PlayRegion.Offset = (len & ((1 << 21) - 1)) * wavebankdata.Alignment; wavebankentry.PlayRegion.Length = (len >> 21) & ((1 << 11) - 1); // workaround because I don't know how to handke the deviation length reader.BaseStream.Seek(wavebank_offset + wavebankdata.EntryMetaDataElementSize, SeekOrigin.Begin); //MYFSEEK(wavebank_offset + wavebankdata.dwEntryMetaDataElementSize); // seek to the next if (current_entry == (wavebankdata.EntryCount - 1)) { // the last track len = wavebankheader.Segments[last_segment].Length; } else { len = ((reader.ReadInt32() & ((1 << 21) - 1)) * wavebankdata.Alignment); } wavebankentry.PlayRegion.Length = len - // next offset wavebankentry.PlayRegion.Offset; // current offset goto wavebank_handle; } if (wavebankheader.Version == 1) { wavebankentry.Format = reader.ReadInt32(); wavebankentry.PlayRegion.Offset = reader.ReadInt32(); wavebankentry.PlayRegion.Length = reader.ReadInt32(); wavebankentry.LoopRegion.Offset = reader.ReadInt32(); wavebankentry.LoopRegion.Length = reader.ReadInt32(); } else { if (wavebankdata.EntryMetaDataElementSize >= 4) { wavebankentry.FlagsAndDuration = reader.ReadInt32(); } if (wavebankdata.EntryMetaDataElementSize >= 8) { wavebankentry.Format = reader.ReadInt32(); } if (wavebankdata.EntryMetaDataElementSize >= 12) { wavebankentry.PlayRegion.Offset = reader.ReadInt32(); } if (wavebankdata.EntryMetaDataElementSize >= 16) { wavebankentry.PlayRegion.Length = reader.ReadInt32(); } if (wavebankdata.EntryMetaDataElementSize >= 20) { wavebankentry.LoopRegion.Offset = reader.ReadInt32(); } if (wavebankdata.EntryMetaDataElementSize >= 24) { wavebankentry.LoopRegion.Length = reader.ReadInt32(); } } if (wavebankdata.EntryMetaDataElementSize < 24) { // work-around if (wavebankentry.PlayRegion.Length != 0) { wavebankentry.PlayRegion.Length = wavebankheader.Segments[last_segment].Length; } }// else if(wavebankdata.EntryMetaDataElementSize > sizeof(WaveBankEntry)) { // skip unused fields // MYFSEEK(wavebank_offset + wavebankdata.EntryMetaDataElementSize); //} wavebank_handle: wavebank_offset += wavebankdata.EntryMetaDataElementSize; wavebankentry.PlayRegion.Offset += playregion_offset; // Parse WAVEBANKMINIWAVEFORMAT MiniFormatTag codec; int chans; int rate; int align; //int bits; if (wavebankheader.Version == 1) { // I'm not 100% sure if the following is correct // version 1: // 1 00000000 000101011000100010 0 001 0 // | | | | | | // | | | | | wFormatTag // | | | | nChannels // | | | ??? // | | nSamplesPerSec // | wBlockAlign // wBitsPerSample codec = (MiniFormatTag)((wavebankentry.Format) & ((1 << 1) - 1)); chans = (wavebankentry.Format >> (1)) & ((1 << 3) - 1); rate = (wavebankentry.Format >> (1 + 3 + 1)) & ((1 << 18) - 1); align = (wavebankentry.Format >> (1 + 3 + 1 + 18)) & ((1 << 8) - 1); //bits = (wavebankentry.Format >> (1 + 3 + 1 + 18 + 8)) & ((1 << 1) - 1); /*} else if(wavebankheader.dwVersion == 23) { // I'm not 100% sure if the following is correct * // version 23: * // 1000000000 001011101110000000 001 1 * // | | | | | * // | | | | ??? * // | | | nChannels? * // | | nSamplesPerSec * // | ??? * // !!!UNKNOWN FORMAT!!! * * //codec = -1; * //chans = (wavebankentry.Format >> 1) & ((1 << 3) - 1); * //rate = (wavebankentry.Format >> 4) & ((1 << 18) - 1); * //bits = (wavebankentry.Format >> 31) & ((1 << 1) - 1); * codec = (wavebankentry.Format ) & ((1 << 1) - 1); * chans = (wavebankentry.Format >> (1) ) & ((1 << 3) - 1); * rate = (wavebankentry.Format >> (1 + 3) ) & ((1 << 18) - 1); * align = (wavebankentry.Format >> (1 + 3 + 18) ) & ((1 << 9) - 1); * bits = (wavebankentry.Format >> (1 + 3 + 18 + 9)) & ((1 << 1) - 1); */ } else { // 0 00000000 000111110100000000 010 01 // | | | | | // | | | | wFormatTag // | | | nChannels // | | nSamplesPerSec // | wBlockAlign // wBitsPerSample codec = (MiniFormatTag)((wavebankentry.Format) & ((1 << 2) - 1)); chans = (wavebankentry.Format >> (2)) & ((1 << 3) - 1); rate = (wavebankentry.Format >> (2 + 3)) & ((1 << 18) - 1); align = (wavebankentry.Format >> (2 + 3 + 18)) & ((1 << 8) - 1); //bits = (wavebankentry.Format >> (2 + 3 + 18 + 8)) & ((1 << 1) - 1); } reader.BaseStream.Seek(wavebankentry.PlayRegion.Offset, SeekOrigin.Begin); byte[] audiodata = reader.ReadBytes(wavebankentry.PlayRegion.Length); // Call the special constuctor on SoundEffect to sort it out. _sounds[current_entry] = new SoundEffect(codec, audiodata, chans, rate, align, wavebankentry.LoopRegion.Offset, wavebankentry.LoopRegion.Length); } audioEngine.Wavebanks[_bankName] = this; IsPrepared = true; }
/// <param name="audioEngine">AudioEngine that will be associated with this sound bank.</param> /// <param name="fileName">Path to a .xsb SoundBank file.</param> public SoundBank(AudioEngine audioEngine, string fileName) { if (audioEngine == null) { throw new ArgumentNullException("audioEngine"); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException("fileName"); } _audioengine = audioEngine; 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 //name = System.Text.Encoding.UTF8.GetString(soundbankreader.ReadBytes(64),0,64).Replace("\0",""); //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), 0, 64).Replace("\0", ""); } //parse cue name table stream.Seek(cueNamesOffset, SeekOrigin.Begin); string[] cueNames = System.Text.Encoding.UTF8.GetString(reader.ReadBytes((int)cueNameTableLen), 0, (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); XactSound sound = new XactSound(audioEngine, this, reader); stream.Seek(oldPosition, SeekOrigin.Begin); _sounds.Add(cueNames [i], new XactSound [] { sound }); _probabilities.Add(cueNames [i], 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); XactSound sound = new XactSound(audioEngine, this, reader); stream.Seek(oldPosition, SeekOrigin.Begin); _sounds.Add(cueNames [numSimpleCues + i], new XactSound [] { sound }); _probabilities.Add(cueNames [numSimpleCues + i], 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(); XactSound[] cueSounds = new XactSound[numEntries]; float[] 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); _sounds.Add(cueNames [numSimpleCues + i], cueSounds); _probabilities.Add(cueNames [numSimpleCues + i], probs); } // Instance limiting reader.ReadByte(); //instanceLimit reader.ReadUInt16(); //fadeInSec, divide by 1000.0f reader.ReadUInt16(); //fadeOutSec, divide by 1000.0f reader.ReadByte(); //instanceFlags } } } }
private WaveBank(AudioEngine audioEngine, string waveBankFilename, bool streaming, int offset, int packetsize) { if (audioEngine == null) { throw new ArgumentNullException("audioEngine"); } if (string.IsNullOrEmpty(waveBankFilename)) { throw new ArgumentNullException("nonStreamingWaveBankFilename"); } // Is this a streaming wavebank? if (streaming) { if (offset != 0) { throw new ArgumentException("We only support a zero offset in streaming banks.", "offset"); } if (packetsize < 2) { throw new ArgumentException("The packet size must be greater than 2.", "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; BinaryReader 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", ""); } else { wavebankdata.BankName = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(64), 0, 64).Replace("\0", ""); } _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); var audiodata = reader.ReadBytes(info.FileLength); // Decode the format information. MiniFormatTag codec; int channels, rate, alignment; DecodeFormat(info.Format, out codec, out channels, out rate, out alignment); // Call the special constuctor on SoundEffect to sort it out. _sounds[i] = new SoundEffect(codec, audiodata, channels, rate, alignment, info.LoopStart, info.LoopLength); } _streams = null; } audioEngine.Wavebanks[_bankName] = this; IsPrepared = true; }