Files
StegoPrint/WaveStream.cs
2017-03-23 19:03:46 +00:00

263 lines
9.6 KiB
C#

using System;
using System.IO;
namespace StegoPrint {
public class WaveStream : Stream, IDisposable {
private Stream m_Stream;
private long m_DataPos;
private int m_Length;
private WaveFormat m_Format;
public WaveFormat Format {
get { return m_Format; }
}
private string ReadChunk (BinaryReader reader) {
byte[] ch = new byte[4];
reader.Read (ch, 0, ch.Length);
return System.Text.Encoding.ASCII.GetString (ch);
}
private void ReadHeader () {
BinaryReader Reader = new BinaryReader (m_Stream);
if (ReadChunk (Reader) != "RIFF")
throw new Exception ("Invalid file format");
Reader.ReadInt32 (); // File length minus first 8 bytes of RIFF description, we don't use it
if (ReadChunk (Reader) != "WAVE")
throw new Exception ("Invalid file format");
if (ReadChunk (Reader) != "fmt ")
throw new Exception ("Invalid file format");
int len = Reader.ReadInt32 ();
if (len < 16) // bad format chunk length
throw new Exception ("Invalid file format");
m_Format = new WaveFormat (22050, 16, 2); // initialize to any format
m_Format.wFormatTag = Reader.ReadInt16 ();
m_Format.nChannels = Reader.ReadInt16 ();
m_Format.nSamplesPerSec = Reader.ReadInt32 ();
m_Format.nAvgBytesPerSec = Reader.ReadInt32 ();
m_Format.nBlockAlign = Reader.ReadInt16 ();
m_Format.wBitsPerSample = Reader.ReadInt16 ();
// advance in the stream to skip the wave format block
len -= 16; // minimum format size
while (len > 0) {
Reader.ReadByte ();
len--;
}
// assume the data chunk is aligned
while (m_Stream.Position < m_Stream.Length && ReadChunk (Reader) != "data")
;
if (m_Stream.Position >= m_Stream.Length)
throw new Exception ("Invalid file format");
m_Length = Reader.ReadInt32 ();
m_DataPos = m_Stream.Position;
Position = 0;
}
/// <summary>ReadChunk(reader) - Changed to CopyChunk(reader, writer)</summary>
/// <param name="reader">source stream</param>
/// <returns>four characters</returns>
private string CopyChunk (BinaryReader reader, BinaryWriter writer) {
byte[] ch = new byte[4];
reader.Read (ch, 0, ch.Length);
//copy the chunk
writer.Write (ch);
return System.Text.Encoding.ASCII.GetString (ch);
}
/// <summary>ReadHeader() - Changed to CopyHeader(destination)</summary>
private void CopyHeader (Stream destinationStream) {
BinaryReader reader = new BinaryReader (m_Stream);
BinaryWriter writer = new BinaryWriter (destinationStream);
if (CopyChunk (reader, writer) != "RIFF")
throw new Exception ("Invalid file format");
writer.Write (reader.ReadInt32 ()); // File length minus first 8 bytes of RIFF description
if (CopyChunk (reader, writer) != "WAVE")
throw new Exception ("Invalid file format");
if (CopyChunk (reader, writer) != "fmt ")
throw new Exception ("Invalid file format");
int len = reader.ReadInt32 ();
if (len < 16) { // bad format chunk length
throw new Exception ("Invalid file format");
} else {
writer.Write (len);
}
m_Format = new WaveFormat (22050, 16, 2); // initialize to any format
m_Format.wFormatTag = reader.ReadInt16 ();
m_Format.nChannels = reader.ReadInt16 ();
m_Format.nSamplesPerSec = reader.ReadInt32 ();
m_Format.nAvgBytesPerSec = reader.ReadInt32 ();
m_Format.nBlockAlign = reader.ReadInt16 ();
m_Format.wBitsPerSample = reader.ReadInt16 ();
//copy format information
writer.Write (m_Format.wFormatTag);
writer.Write (m_Format.nChannels);
writer.Write (m_Format.nSamplesPerSec);
writer.Write (m_Format.nAvgBytesPerSec);
writer.Write (m_Format.nBlockAlign);
writer.Write (m_Format.wBitsPerSample);
// advance in the stream to skip the wave format block
len -= 16; // minimum format size
writer.Write (reader.ReadBytes (len));
len = 0;
/*while (len > 0)
{
reader.ReadByte();
len--;
}*/
// assume the data chunk is aligned
while (m_Stream.Position < m_Stream.Length && CopyChunk (reader, writer) != "data")
;
if (m_Stream.Position >= m_Stream.Length)
throw new Exception ("Invalid file format");
m_Length = reader.ReadInt32 ();
writer.Write (m_Length);
m_DataPos = m_Stream.Position;
Position = 0;
}
/// <summary>Write a new header</summary>
public static Stream CreateStream (Stream waveData, WaveFormat format) {
MemoryStream stream = new MemoryStream ();
BinaryWriter writer = new BinaryWriter (stream);
writer.Write (System.Text.Encoding.ASCII.GetBytes ("RIFF".ToCharArray ()));
writer.Write ((Int32) (waveData.Length + 36)); //File length minus first 8 bytes of RIFF description
writer.Write (System.Text.Encoding.ASCII.GetBytes ("WAVEfmt ".ToCharArray ()));
writer.Write ((Int32) 16); //length of following chunk: 16
writer.Write ((Int16) format.wFormatTag);
writer.Write ((Int16) format.nChannels);
writer.Write ((Int32) format.nSamplesPerSec);
writer.Write ((Int32) format.nAvgBytesPerSec);
writer.Write ((Int16) format.nBlockAlign);
writer.Write ((Int16) format.wBitsPerSample);
writer.Write (System.Text.Encoding.ASCII.GetBytes ("data".ToCharArray ()));
writer.Write ((Int32) waveData.Length);
waveData.Seek (0, SeekOrigin.Begin);
byte[] b = new byte[waveData.Length];
waveData.Read (b, 0, (int) waveData.Length);
writer.Write (b);
writer.Seek (0, SeekOrigin.Begin);
return stream;
}
public WaveStream (Stream sourceStream, Stream destinationStream) {
m_Stream = sourceStream;
CopyHeader (destinationStream);
}
public WaveStream (Stream sourceStream) {
m_Stream = sourceStream;
ReadHeader ();
}
~WaveStream () {
Dispose ();
}
public new void Dispose () {
base.Dispose ();
GC.SuppressFinalize (this);
}
public override bool CanRead {
get { return true; }
}
public override bool CanSeek {
get { return true; }
}
public override bool CanWrite {
get { return false; }
}
public override long Length {
get { return m_Length; }
}
/// <summary>Length of the data (in samples)</summary>
public long CountSamples {
get { return (long) ((m_Length - m_DataPos) / (m_Format.wBitsPerSample / 8)); }
}
public override long Position {
get { return m_Stream.Position - m_DataPos; }
set { Seek (value, SeekOrigin.Begin); }
}
public override void Flush () { }
public override void SetLength (long len) {
throw new InvalidOperationException ();
}
public override long Seek (long pos, SeekOrigin o) {
switch (o) {
case SeekOrigin.Begin:
m_Stream.Position = pos + m_DataPos;
break;
case SeekOrigin.Current:
m_Stream.Seek (pos, SeekOrigin.Current);
break;
case SeekOrigin.End:
m_Stream.Position = m_DataPos + m_Length - pos;
break;
}
return this.Position;
}
public override int Read (byte[] buf, int ofs, int count) {
int toread = (int) Math.Min (count, m_Length - Position);
return m_Stream.Read (buf, ofs, toread);
}
/// <summary>Read - Changed to Copy</summary>
/// <param name="buf">Buffer to receive the data</param>
/// <param name="ofs">Offset</param>
/// <param name="count">Count of bytes to read</param>
/// <param name="destination">Where to copy the buffer</param>
/// <returns>Count of bytes actually read</returns>
public int Copy (byte[] buf, int ofs, int count, Stream destination) {
int toread = (int) Math.Min (count, m_Length - Position);
int read = m_Stream.Read (buf, ofs, toread);
destination.Write (buf, ofs, read);
if (m_Stream.Position != destination.Position) {
Console.WriteLine ();
}
return read;
}
public override void Write (byte[] buf, int ofs, int count) {
throw new InvalidOperationException ();
}
}
}