From b764ebdef37929a26296236b81055ced49b44d73 Mon Sep 17 00:00:00 2001
From: absoluteAquarian <30454621+absoluteAquarian@users.noreply.github.com>
Date: Tue, 14 Jan 2025 16:15:01 -0600
Subject: [PATCH] fixed an oversight where metadata wasn't ignored for .wav
audio streams, cleaned up the audio initialization in FormatWav
---
API/MonoSoundLibrary.cs | 2 +-
Audio/FormatWav.cs | 254 +++++++++-----------------------------
Audio/WAVEReader.cs | 170 +++++++++++++++++++++++++
CHANGELOG.md | 5 +-
MonoSound.csproj | 6 +-
Streaming/AudioStreams.cs | 17 +--
TODO.txt | 3 -
7 files changed, 245 insertions(+), 212 deletions(-)
create mode 100644 Audio/WAVEReader.cs
diff --git a/API/MonoSoundLibrary.cs b/API/MonoSoundLibrary.cs
index 62d4664..50553f8 100644
--- a/API/MonoSoundLibrary.cs
+++ b/API/MonoSoundLibrary.cs
@@ -58,7 +58,7 @@ public static class MonoSoundLibrary {
internal const string VersionLiteral_FilterOverhaul = "1.8";
///
- public const string VersionLiteral = "1.8";
+ public const string VersionLiteral = "1.8.0.1";
///
/// The version for MonoSound
diff --git a/Audio/FormatWav.cs b/Audio/FormatWav.cs
index 91488bb..a3cb87f 100644
--- a/Audio/FormatWav.cs
+++ b/Audio/FormatWav.cs
@@ -18,9 +18,14 @@ public sealed class FormatWav : IDisposable {
// http://soundfile.sapp.org/doc/WaveFormat/
// https://medium.com/swlh/reversing-a-wav-file-in-c-482fc3dfe3c4
+ private const int RIFF_HEADER_SIZE = 12; // "RIFF" + Size + "WAVE"
+ private const int SUBCHUNK_HEADER_SIZE = 8;
+ private const int FMT_SUBCHUNK_DATA_LENGTH = 16;
+
private byte[] data;
+ private int _fmtSubchunkOffset = RIFF_HEADER_SIZE;
+ private int _dataSubchunkOffset = RIFF_HEADER_SIZE + SUBCHUNK_HEADER_SIZE + FMT_SUBCHUNK_DATA_LENGTH;
- //Header data is always the first 44 bytes
///
/// "RIFF" if the file stores its values as Little-Endian, "RIFX" otherwise
///
@@ -36,51 +41,51 @@ public sealed class FormatWav : IDisposable {
///
/// The format chunk marker. Always set to "fmt "
///
- public string FormatChunkMarker => Encoding.UTF8.GetString(data, 12, 4);
+ public string FormatChunkMarker => Encoding.UTF8.GetString(data, _fmtSubchunkOffset, 4);
///
/// The length of the format data in bytes
///
- public int FormatLength => BitConverter.ToInt32(data, 16);
+ public int FormatLength => BitConverter.ToInt32(data, _fmtSubchunkOffset + 4);
///
/// The audio format each sample is saved as.
/// PCM is 1
/// Any other values are assumed to be some other form of compression
///
- public short FormatType => BitConverter.ToInt16(data, 20);
+ public short FormatType => BitConverter.ToInt16(data, _fmtSubchunkOffset + 8);
///
/// What channels this WAV sound will use.
/// Mono is 1
/// Stereo is 2
///
- public short ChannelCount => BitConverter.ToInt16(data, 22);
+ public short ChannelCount => BitConverter.ToInt16(data, _fmtSubchunkOffset + 10);
///
/// How many samples are played per second. Measured in Hertz (Hz)
///
- public int SampleRate => BitConverter.ToInt32(data, 24);
+ public int SampleRate => BitConverter.ToInt32(data, _fmtSubchunkOffset + 12);
///
/// How many bytes are played per second
/// Usually set to: SampleRate * BitsPerSample * ChannelCount / 8
///
- public int ByteRate => BitConverter.ToInt32(data, 28);
+ public int ByteRate => BitConverter.ToInt32(data, _fmtSubchunkOffset + 16);
///
/// The number of bytes per sample including all channels
/// Usually set to: BitsPerSample * ChannelCount / 8
///
- public short BlockAlign => BitConverter.ToInt16(data, 32);
+ public short BlockAlign => BitConverter.ToInt16(data, _fmtSubchunkOffset + 20);
///
/// How many bits PER CHANNEL are in one sample
///
- public short BitsPerSample => BitConverter.ToInt16(data, 34);
+ public short BitsPerSample => BitConverter.ToInt16(data, _fmtSubchunkOffset + 22);
///
/// The data chunk marker. Always set to "data"
///
- public string DataChunkMarker => Encoding.UTF8.GetString(data, 36, 4);
+ public string DataChunkMarker => Encoding.UTF8.GetString(data, _fmtSubchunkOffset + 24, 4);
///
/// The length of the sample data in bytes
///
public int DataLength {
- get => BitConverter.ToInt32(data, 40);
- private set => BitConverter.GetBytes(value).CopyTo(data, 40);
+ get => BitConverter.ToInt32(data, _dataSubchunkOffset + 4);
+ private set => BitConverter.GetBytes(value).CopyTo(data, _dataSubchunkOffset + 4);
}
///
@@ -105,7 +110,7 @@ public WavSample[] GetSamples() {
///
public byte[] GetSoundBytes() {
byte[] audioData = new byte[DataLength];
- Buffer.BlockCopy(data, 44, audioData, 0, audioData.Length);
+ Buffer.BlockCopy(data, _dataSubchunkOffset + 8, audioData, 0, audioData.Length);
return audioData;
}
@@ -269,39 +274,14 @@ public static FormatWav FromFileOGG(Stream readStream) {
using VorbisReader reader = new VorbisReader(readStream, closeStreamOnDispose: true);
- byte[] fmtChunk = new byte[16];
-
- //Type of Format
- fmtChunk[0] = 0x01;
-
- //Number of Channels
- byte[] arr = BitConverter.GetBytes((short)reader.Channels);
- fmtChunk[2] = arr[0];
- fmtChunk[3] = arr[1];
-
- //Samples per Second
- arr = BitConverter.GetBytes(reader.SampleRate);
- fmtChunk[4] = arr[0];
- fmtChunk[5] = arr[1];
- fmtChunk[6] = arr[2];
- fmtChunk[7] = arr[3];
-
- //Bytes per Second
- arr = BitConverter.GetBytes(reader.SampleRate * reader.Channels * 2);
- fmtChunk[8] = arr[0];
- fmtChunk[9] = arr[1];
- fmtChunk[10] = arr[2];
- fmtChunk[11] = arr[3];
-
- //Block Align
- arr = BitConverter.GetBytes((short)(reader.Channels * 2));
- fmtChunk[12] = arr[0];
- fmtChunk[13] = arr[1];
-
- //Bits per Sample
- arr = BitConverter.GetBytes((short)16);
- fmtChunk[14] = arr[0];
- fmtChunk[15] = arr[1];
+ byte[] fmtChunk = [
+ 0x01, 0x00,
+ .. BitConverter.GetBytes((short)reader.Channels),
+ .. BitConverter.GetBytes(reader.SampleRate),
+ .. BitConverter.GetBytes(reader.SampleRate * reader.Channels * 2),
+ .. BitConverter.GetBytes((short)(reader.Channels * 2)),
+ 0x10, 0x00 // 16-bit PCM
+ ];
//Read the samples
float[] buffer = new float[reader.SampleRate / 10 * reader.Channels];
@@ -378,54 +358,18 @@ private static IEnumerable AsBytes(string word) {
}
internal static FormatWav FromDecompressorData(byte[] sampleData, byte[] fmtChunk) {
- byte[] addon = new byte[44];
- addon[0] = (byte)'R';
- addon[1] = (byte)'I';
- addon[2] = (byte)'F';
- addon[3] = (byte)'F';
-
- //Excluded: Total file size
-
- addon[8] = (byte)'W';
- addon[9] = (byte)'A';
- addon[10] = (byte)'V';
- addon[11] = (byte)'E';
-
- addon[12] = (byte)'f';
- addon[13] = (byte)'m';
- addon[14] = (byte)'t';
- addon[15] = (byte)' ';
-
- //Format header length
- addon[16] = 16;
-
- //Type of Format, Number of Channels, Samples per Second, Bytes per Second, Block Align, Bits per Sample
- Buffer.BlockCopy(fmtChunk, 0, addon, 20, 16);
-
- addon[36] = (byte)'d';
- addon[37] = (byte)'a';
- addon[38] = (byte)'t';
- addon[39] = (byte)'a';
-
- byte[] arr = BitConverter.GetBytes(sampleData.Length);
- addon[40] = arr[0];
- addon[41] = arr[1];
- addon[42] = arr[2];
- addon[43] = arr[3];
-
- byte[] actualStream = new byte[addon.Length + sampleData.Length];
-
- //Copy the data
- Buffer.BlockCopy(addon, 0, actualStream, 0, addon.Length);
- Buffer.BlockCopy(sampleData, 0, actualStream, addon.Length, sampleData.Length);
-
- arr = BitConverter.GetBytes(actualStream.Length);
- actualStream[4] = arr[0];
- actualStream[5] = arr[1];
- actualStream[6] = arr[2];
- actualStream[7] = arr[3];
+ byte[] header = [
+ .. AsBytes("RIFF"),
+ .. BitConverter.GetBytes(RIFF_HEADER_SIZE + SUBCHUNK_HEADER_SIZE + FMT_SUBCHUNK_DATA_LENGTH + SUBCHUNK_HEADER_SIZE + sampleData.Length),
+ .. AsBytes("WAVE"),
+ .. AsBytes("fmt "),
+ 0x10, 0x00, 0x00, 0x00, // Length of format chunk = 16 bytes
+ .. fmtChunk,
+ .. AsBytes("data"),
+ .. BitConverter.GetBytes(sampleData.Length) // Sample data length
+ ];
- return FromBytes(actualStream);
+ return FromBytes([ .. header, .. sampleData ]);
}
///
@@ -450,39 +394,14 @@ public static FormatWav FromFileMP3(Stream readStream) {
try {
using MP3Stream stream = new MP3Stream(readStream);
- byte[] fmtChunk = new byte[16];
-
- //Type of Format
- fmtChunk[0] = 0x01;
-
- //Number of Channels
- byte[] arr = BitConverter.GetBytes((short)AudioChannels.Stereo); //MP3 decoder forces the samples to align to stereo
- fmtChunk[2] = arr[0];
- fmtChunk[3] = arr[1];
-
- //Samples per Second
- arr = BitConverter.GetBytes(stream.Frequency);
- fmtChunk[4] = arr[0];
- fmtChunk[5] = arr[1];
- fmtChunk[6] = arr[2];
- fmtChunk[7] = arr[3];
-
- //Bytes per Second
- arr = BitConverter.GetBytes(stream.Frequency * 4);
- fmtChunk[8] = arr[0];
- fmtChunk[9] = arr[1];
- fmtChunk[10] = arr[2];
- fmtChunk[11] = arr[3];
-
- //Block Align
- arr = BitConverter.GetBytes((short)4);
- fmtChunk[12] = arr[0];
- fmtChunk[13] = arr[1];
-
- //Bits per Sample
- arr = BitConverter.GetBytes((short)16);
- fmtChunk[14] = arr[0];
- fmtChunk[15] = arr[1];
+ byte[] fmtChunk = [
+ 0x01, 0x00, // PCM format
+ 0x02, 0x00, // MP3 decoder forces the samples to align to Stereo
+ .. BitConverter.GetBytes(stream.Frequency),
+ .. BitConverter.GetBytes(stream.Frequency * 4),
+ 0x04, 0x00, // Block Align is always 4 bytes
+ 0x10, 0x00 // 16-bit PCM
+ ];
//Read the samples
byte[] sampleWrite = new byte[1024];
@@ -513,7 +432,7 @@ public static FormatWav FromFileMP3(Stream readStream) {
/// Loads a from a .wav byte stream
///
public static FormatWav FromBytes(byte[] data) {
- if (data.Length < 44)
+ if (data.Length < RIFF_HEADER_SIZE + SUBCHUNK_HEADER_SIZE + FMT_SUBCHUNK_DATA_LENGTH + SUBCHUNK_HEADER_SIZE)
throw new ArgumentException("Data was too short to contain a header.", nameof(data));
FormatWav wav = new FormatWav() {
@@ -522,50 +441,11 @@ public static FormatWav FromBytes(byte[] data) {
//Verify that the input data was correct
try {
-HeaderCheckStart:
-
- string eHeader = wav.EndianHeader;
- if (eHeader != "RIFF" && eHeader != "RIFX")
- throw new Exception("Endian header string was not \"RIFF\" nor \"RIFX\".");
-
- if (wav.FileTypeHeader != "WAVE")
- throw new Exception("File type header string was not \"WAVE\".");
-
- if (wav.FormatChunkMarker != "fmt ")
- throw new Exception("Format chunk header string was not \"fmt \".");
-
- if (wav.DataChunkMarker != "data") {
- //If the data chunk marker was instead "LIST", then there's metadata in the WAV file
- //That metadata is completely irrelevant for MonoSound, so the data array needs to be rebuilt with the "LIST" chunk missing
- if (wav.DataChunkMarker == "LIST") {
- int infoLength = wav.DataLength;
+ using MemoryStream ms = new MemoryStream(data);
+ using WAVEReader reader = new WAVEReader(ms);
- byte[] overwrite = new byte[data.Length - infoLength];
- Buffer.BlockCopy(data, 0, overwrite, 0, 36);
-
- int afterInfo = 44 + infoLength;
- Buffer.BlockCopy(data, afterInfo, overwrite, 36, data.Length - afterInfo);
-
- byte[] bytes = BitConverter.GetBytes(overwrite.Length);
- overwrite[4] = bytes[0];
- overwrite[5] = bytes[1];
- overwrite[6] = bytes[2];
- overwrite[7] = bytes[3];
-
- wav.data = overwrite;
-
- goto HeaderCheckStart;
- }
-
- throw new Exception("Data chunk header string was not \"data\".");
- }
-
- if (wav.data.Length != wav.Size)
- throw new Exception("File size did not match stored size.");
-
- int sampleRate = wav.SampleRate;
- if (sampleRate < 8000 || sampleRate > 48000)
- throw new Exception("Sample rate was outside the range of valid values.");
+ wav._fmtSubchunkOffset = reader.ReadFormat(out _, out _, out _, out _, out _);
+ wav._dataSubchunkOffset = reader.ReadDataHeader(out _);
} catch (Exception ex) {
throw new ArgumentException("Data was invalid for the WAV format.", nameof(data), ex);
}
@@ -574,32 +454,14 @@ public static FormatWav FromBytes(byte[] data) {
}
internal static FormatWav FromSoundEffectConstructor(XACT.MiniFormatTag codec, byte[] buffer, int channels, int sampleRate, int blockAlignment, int loopStart, int loopLength) {
- //WaveBank sounds always have 16 bits/sample for some reason
- const int bitsPerSample = 16;
-
- byte[] fmtChunk = new byte[16];
- var bytes = BitConverter.GetBytes((short)1); //Force the PCM encoding... Others aren't allowed
- fmtChunk[0] = bytes[0];
- fmtChunk[1] = bytes[1];
- bytes = BitConverter.GetBytes((short)channels);
- fmtChunk[2] = bytes[0];
- fmtChunk[3] = bytes[1];
- bytes = BitConverter.GetBytes(sampleRate);
- fmtChunk[4] = bytes[0];
- fmtChunk[5] = bytes[1];
- fmtChunk[6] = bytes[2];
- fmtChunk[7] = bytes[3];
- bytes = BitConverter.GetBytes(sampleRate * channels * bitsPerSample / 8);
- fmtChunk[8] = bytes[0];
- fmtChunk[9] = bytes[1];
- fmtChunk[10] = bytes[2];
- fmtChunk[11] = bytes[3];
- bytes = BitConverter.GetBytes((short)blockAlignment);
- fmtChunk[12] = bytes[0];
- fmtChunk[13] = bytes[1];
- bytes = BitConverter.GetBytes((short)bitsPerSample);
- fmtChunk[14] = bytes[0];
- fmtChunk[15] = bytes[1];
+ byte[] fmtChunk = [
+ 0x01, 0x00, // Force the PCM encoding... Others aren't allowed
+ .. BitConverter.GetBytes((short)channels),
+ .. BitConverter.GetBytes(sampleRate),
+ .. BitConverter.GetBytes(sampleRate * channels * 2),
+ .. BitConverter.GetBytes((short)blockAlignment),
+ 0x10, 0x00 // WaveBank sounds always have 16 bits/sample for some reason
+ ];
return FromDecompressorData(buffer, fmtChunk);
}
diff --git a/Audio/WAVEReader.cs b/Audio/WAVEReader.cs
new file mode 100644
index 0000000..3d78e57
--- /dev/null
+++ b/Audio/WAVEReader.cs
@@ -0,0 +1,170 @@
+using Microsoft.Xna.Framework.Audio;
+using System;
+using System.IO;
+using System.Text;
+
+namespace MonoSound.Audio {
+ ///
+ /// A wrapper around for reading .wav files
+ ///
+ public class WAVEReader : StreamReader {
+ private const int MAGIC_RIFF = 0x46464952; // ASCII for "RIFF" in little-endian
+ private const int MAGIC_WAVE = 0x45564157; // ASCII for "WAVE" in little-endian
+
+ ///
+ /// Creates a new from a
+ ///
+ /// The to wrap
+ public WAVEReader(Stream stream) : base(stream, detectEncodingFromByteOrderMarks: false, leaveOpen: true) { // Important! The stream must be left open so that the caller can read the sample data afterwards
+ // Verify that the file header is correct
+ Span header = stackalloc byte[4];
+ int bytes = BaseStream.Read(header);
+
+ if (bytes != 4 || BitConverter.ToInt32(header) != MAGIC_RIFF)
+ throw new InvalidDataException("File is not a RIFF file");
+
+ // Skip the file size
+ BaseStream.Seek(4, SeekOrigin.Current);
+
+ bytes = BaseStream.Read(header);
+
+ if (bytes != 4 || BitConverter.ToInt32(header) != MAGIC_WAVE)
+ throw new InvalidDataException("File is not a WAVE file");
+ }
+
+ ///
+ /// Parses the "fmt " subchunk of the WAVE file, skipping any other subchunks that may be present
+ ///
+ /// The channel count
+ /// The sample rate
+ /// The number of bytes read per second
+ /// The size of one sample in bytes
+ /// The number of bits per seample
+ ///
+ ///
+ /// The offset of the "fmt " subchunk in the WAVE file
+ public int ReadFormat(out AudioChannels channels, out int sampleRate, out int byteRate, out short blockAlign, out short bitsPerSample) {
+ // Skip forward until we find the "fmt " subchunk
+ string subchunkID;
+ int subchunkSize;
+
+ ReadSubchunk:
+ int offset = (int)BaseStream.Position;
+ ReadSubchunkHeader(out subchunkID, out subchunkSize);
+ if (subchunkID != "fmt ") {
+ // Skip the subchunk data
+ BaseStream.Seek(subchunkSize, SeekOrigin.Current);
+ goto ReadSubchunk;
+ }
+
+ if (subchunkSize != 16)
+ throw new InvalidDataException($"Subchunk size is {subchunkSize}, expected 16");
+
+ // Read the format
+ Span format2 = stackalloc byte[2];
+ int bytes = BaseStream.Read(format2);
+ if (bytes != 2)
+ throw new EndOfStreamException("Failed to read audio format");
+ if (BitConverter.ToInt16(format2) != 1)
+ throw new InvalidDataException("MonoSound does not support compressed WAVE audio formats");
+
+ // Read the channel count
+ bytes = BaseStream.Read(format2);
+ if (bytes != 2)
+ throw new EndOfStreamException("Failed to read channel count");
+
+ channels = (AudioChannels)BitConverter.ToInt16(format2);
+ if (channels != AudioChannels.Mono && channels != AudioChannels.Stereo)
+ throw new InvalidDataException("MonoSound only supports Mono and Stereo audio");
+
+ // Read the sample rate
+ Span format4 = stackalloc byte[4];
+ bytes = BaseStream.Read(format4);
+ if (bytes != 4)
+ throw new EndOfStreamException("Failed to read sample rate");
+
+ sampleRate = BitConverter.ToInt32(format4);
+ if (sampleRate < 8000 || sampleRate > 48000)
+ throw new InvalidDataException("MonoSound only supports sample rates between 8000 and 48000 Hz");
+
+ // Read the rest of the header since they rely on the bits per sample being read, and that's the last field
+ bytes = BaseStream.Read(format4);
+ if (bytes != 4)
+ throw new EndOfStreamException("Failed to read byte rate");
+
+ byteRate = BitConverter.ToInt32(format4);
+
+ bytes = BaseStream.Read(format2);
+ if (bytes != 2)
+ throw new EndOfStreamException("Failed to read block align");
+
+ blockAlign = BitConverter.ToInt16(format2);
+
+ bytes = BaseStream.Read(format2);
+ if (bytes != 2)
+ throw new EndOfStreamException("Failed to read bits per sample");
+
+ bitsPerSample = BitConverter.ToInt16(format2);
+ if (bitsPerSample != 16 && bitsPerSample != 24)
+ throw new InvalidDataException("MonoSound only supports 16-bit and 24-bit PCM audio");
+
+ int bytesPerSample = bitsPerSample / 8;
+
+ // Check the byte rate and block align
+ int expectedByteRate = sampleRate * (int)channels * bytesPerSample;
+ if (byteRate != expectedByteRate)
+ throw new InvalidDataException($"Byte rate is {byteRate}, expected {expectedByteRate}");
+
+ int expectedBlockAlign = (int)channels * bytesPerSample;
+ if (blockAlign != expectedBlockAlign)
+ throw new InvalidDataException($"Block align is {blockAlign}, expected {expectedBlockAlign}");
+
+ return offset;
+ }
+
+ ///
+ /// Parses the header for the "data" subchunk of the WAVE file, skipping any other subchunks that may be present
+ ///
+ /// The size of the sample data
+ ///
+ /// The offset of the "data" subchunk in the WAVE file
+ public int ReadDataHeader(out int dataSize) {
+ ReadSubchunk:
+ int offset = (int)BaseStream.Position;
+ ReadSubchunkHeader(out string subchunkID, out int subchunkSize);
+ if (subchunkID != "data") {
+ // Skip the subchunk data
+ BaseStream.Seek(subchunkSize, SeekOrigin.Current);
+ goto ReadSubchunk;
+ }
+
+ dataSize = subchunkSize;
+
+ return offset;
+ }
+
+ ///
+ /// Reads the header for the next subchunk in the stream
+ ///
+ /// The name of the subchunk
+ /// The size of the subchunk in bytes
+ ///
+ public void ReadSubchunkHeader(out string subchunkID, out int subchunkSize) {
+ Span header = stackalloc byte[4];
+ int bytes = BaseStream.Read(header);
+
+ if (bytes != 4)
+ throw new EndOfStreamException("Failed to read subchunk ID");
+
+ subchunkID = Encoding.ASCII.GetString(header);
+
+ // Coincidentally, both the ID and subchunk length are 4 bytes long
+ bytes = BaseStream.Read(header);
+
+ if (bytes != 4)
+ throw new EndOfStreamException("Failed to read subchunk size");
+
+ subchunkSize = BitConverter.ToInt32(header);
+ }
+ }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c13530d..038a56d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,7 @@
-## v1.8
+## v1.8.0.1
+- Fixed an oversight where audio streams could not properly parse certain `.wav` files due to unexpected metadata
+
+## v1.8
**Summary:**
- Many bug fixes
- Complete overhaul of the sound filter system
diff --git a/MonoSound.csproj b/MonoSound.csproj
index 4b29353..d093d5b 100644
--- a/MonoSound.csproj
+++ b/MonoSound.csproj
@@ -6,14 +6,14 @@
false
True
false
- 1.8
+ 1.8.0.1
True
John Baglio
- A library built for use with MonoGame DesktopGL projects.
+ A library built for use with MonoGame DesktopGL and Android projects.
This project allows for easy loading of SoundEffect instances without the content pipeline, along with XACT wavebank sounds.
Additional features include streaming audio from files and applying SoLoud sound filters to sounds.
The repository for MonoGame can be viewed at https://github.com/MonoGame/MonoGame
- Copyright © John Baglio 2022
+ Copyright © John Baglio 2024
README.md
https://github.com/absoluteAquarian/MonoSound
diff --git a/Streaming/AudioStreams.cs b/Streaming/AudioStreams.cs
index 6bea50b..c5921ea 100644
--- a/Streaming/AudioStreams.cs
+++ b/Streaming/AudioStreams.cs
@@ -1,5 +1,6 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
+using MonoSound.Audio;
using MonoSound.XACT;
using System;
using System.IO;
@@ -37,15 +38,15 @@ protected WavStream(Stream stream, AudioType typeOverride) : base(stream, typeOv
///
protected override void Initialize() {
- //Read the header
- byte[] header = new byte[44];
- underlyingStream.Read(header, 0, 44);
- Channels = (AudioChannels)BitConverter.ToInt16(header, 22);
- SampleRate = BitConverter.ToInt32(header, 24);
- BitsPerSample = BitConverter.ToInt16(header, 34);
- TotalBytes = BitConverter.ToInt32(header, 40);
+ // Read the header data
+ using WAVEReader reader = new WAVEReader(underlyingStream);
+ reader.ReadFormat(out AudioChannels channels, out int sampleRate, out _, out _, out short bitsPerSample);
+ reader.ReadDataHeader(out int totalBytes);
- sampleReadStart = underlyingStream.Position;
+ Channels = channels;
+ SampleRate = sampleRate;
+ BitsPerSample = bitsPerSample;
+ TotalBytes = totalBytes;
InitSound(); // REQUIRED! This initializes PlayingSound and Metrics
}
diff --git a/TODO.txt b/TODO.txt
index 32648db..16d62fa 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -2,8 +2,5 @@
- Implement the rest of SoLoud's sound filters
-Filter Testing:
-- test low pass filter with various frequencies and resonance values
-
Feature Requests:
- none
\ No newline at end of file