|
|
||||
|
vbAccelerator - Contents of code file: WaveStreamReader.csThis file is part of the download WaveStream CSharp, which is described in the article WaveStreamReader and WaveStreamWriter. using System;
using System.IO;
using System.Runtime.InteropServices;
namespace vbAccelerator.Audio.WaveStream
{
/// <summary>
/// A <c>Stream</c> implementation which wraps the Windows Multi-media
/// IO to read a Wave file.
/// </summary>
public class WaveStreamReader : Stream, IDisposable
{
private string waveFile;
private IntPtr hMmio = IntPtr.Zero;
private bool disposed = false;
private WinMMInterop.WAVEFORMATEX format;
private int dataOffset = 0;
private int audioLength = 0;
/// <summary>
/// Default constructor
/// </summary>
public WaveStreamReader() : base()
{
}
/// <summary>
/// Constructor for a particular wave file.
/// </summary>
/// <param name="file">File name of the wave file to read</param>
public WaveStreamReader(string file) : this()
{
Filename = file;
}
/// <summary>
/// Destructor: ensures that the wave file handle is closed.
/// </summary>
~WaveStreamReader()
{
Dispose(false);
}
/// <summary>
/// Clears up resources associated with this class.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the Multi-media IO handle to the wave file.
/// </summary>
protected virtual IntPtr Handle
{
get
{
return hMmio;
}
}
/// <summary>
/// Clears up resources associated with this class.
/// </summary>
/// <param name="disposing"><code>true</code> if disposing from the
<c>Dispose</c>
/// method, otherwise <c>false</c>.</param>
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// nothing to do
}
CloseWaveFile();
disposed = true;
}
}
/// <summary>
/// Gets/sets the wave file name.
/// </summary>
public string Filename
{
get
{
return waveFile;
}
set
{
if (hMmio != IntPtr.Zero)
{
CloseWaveFile();
}
waveFile = value;
OpenWaveFile();
}
}
/// <summary>
/// Gets the number of audio channels in the file.
/// </summary>
public short Channels
{
get
{
return format.nChannels;
}
}
/// <summary>
/// Gets the sample frequency of the file.
/// </summary>
public int SamplingFrequency
{
get
{
return format.nSamplesPerSec;
}
}
/// <summary>
/// Gets the number of bits per sample in the wave file.
/// </summary>
public short BitsPerSample
{
get
{
return format.wBitsPerSample;
}
}
/// <summary>
/// Gets whether the stream can be read or not (true whenever a wave file
/// is open).
/// </summary>
public override bool CanRead
{
get
{
return (hMmio != IntPtr.Zero);
}
}
/// <summary>
/// Gets whether the stream is seekable or not (true whenever a wave file
/// is open)
/// </summary>
public override bool CanSeek
{
get
{
return (hMmio != IntPtr.Zero);
}
}
/// <summary>
/// Returns false; this is a read-only stream
/// </summary>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// Throws an exception; this is a read-only stream
/// </summary>
/// <exception cref="InvalidOperationException">Thrown
exception</exception>
public override void Flush()
{
throw new InvalidOperationException(
"This class can only read files. Use the WaveStreamWriter class to
write files.");
}
/// <summary>
/// Gets the length of this wave file, in bytes.
/// </summary>
public override long Length
{
get
{
return audioLength;
}
}
/// <summary>
/// Throws an exception; this is a read-only stream
/// </summary>
/// <exception cref="InvalidOperationException">Thrown
exception</exception>
public override void SetLength(long length)
{
throw new InvalidOperationException(
"This class can only read files. Use the WaveStreamWriter class to
write files.");
}
/// <summary>
/// Gets/sets the position within the wave file.
/// </summary>
public override long Position
{
get
{
return 0;
}
set
{
Seek(value + dataOffset, SeekOrigin.Begin);
}
}
/// <summary>
/// Reads <c>count</c> bytes into the buffer.
/// </summary>
/// <param name="buffer">Buffer to read into</param>
/// <param name="count">Number of bytes to read</param>
/// <returns>Number of bytes read.</returns>
public virtual int Read ( byte[] buffer, int count )
{
return Read(buffer, 0, count);
}
/// <summary>
/// Reads <c>count</c> bytes into the buffer.
/// </summary>
/// <param name="buffer">Buffer to read into</param>
/// <param name="count">Number of bytes to read</param>
/// <param name="offset">Offset from the current file position to start
reading from</param>
/// <returns>Number of bytes read.</returns>
public override int Read ( byte[] buffer , int offset , int count )
{
if (hMmio == IntPtr.Zero)
{
throw new InvalidOperationException("No wave data is open");
}
if (offset != 0)
{
Seek((long) offset, SeekOrigin.Current);
}
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr ptrBuffer = handle.AddrOfPinnedObject();
int dataRemaining = (dataOffset + audioLength -
WinMMInterop.mmioSeek(hMmio, 0, WinMMInterop.SEEK_CUR));
int read = 0;
if (count < dataRemaining)
{
read = WinMMInterop.mmioRead(hMmio, ptrBuffer, count);
}
else if (dataRemaining > 0)
{
read = WinMMInterop.mmioRead(hMmio, ptrBuffer, dataRemaining);
}
if (handle.IsAllocated)
{
handle.Free();
}
return read;
}
/// <summary>
/// Reads <c>count</c> shorts into the buffer.
/// </summary>
/// <param name="buffer">Buffer to read into</param>
/// <param name="count">Number of shorts to read</param>
/// <returns>Number of bytes read.</returns>
public virtual int Read (short[] buffer, int count)
{
return Read(buffer, 0, count);
}
/// <summary>
/// Reads <c>count</c> shorts into the buffer.
/// </summary>
/// <param name="buffer">Buffer to read into</param>
/// <param name="count">Number of shorts to read</param>
/// <param name="offset">Offset in shorts (2 bytes) from the current file
position to start
/// reading from</param>
/// <returns>Number of bytes read.</returns>
public virtual int Read ( short[] buffer, int offset, int count )
{
if (hMmio == IntPtr.Zero)
{
throw new InvalidOperationException("No wave data is open");
}
if (offset != 0)
{
Seek((long) (offset / 2), SeekOrigin.Current);
}
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr ptrBuffer = handle.AddrOfPinnedObject();
int dataRemaining = (dataOffset + audioLength -
WinMMInterop.mmioSeek(hMmio, 0, WinMMInterop.SEEK_CUR)) / 2;
int read = 0;
if (count < dataRemaining)
{
read = WinMMInterop.mmioRead(hMmio, ptrBuffer, count * 2);
}
else if (dataRemaining > 0)
{
read = WinMMInterop.mmioRead(hMmio, ptrBuffer, dataRemaining * 2);
}
if (handle.IsAllocated)
{
handle.Free();
}
return read / 2;
}
/// <summary>
/// Throws an exception; this is a read-only stream
/// </summary>
/// <exception cref="InvalidOperationException">Thrown
exception</exception>
public override void Write( byte[] buffer, int offset, int count)
{
throw new InvalidOperationException(
"This class can only read files. Use the WaveStreamWriter class to
write wave files.");
}
/// <summary>
/// Seeks to the specified position in the stream, in bytes
/// </summary>
/// <param name="position">Position to seek to</param>
/// <param name="origin">Specifies the starting postion of the
seek</param>
public override long Seek(long position, SeekOrigin origin)
{
if (hMmio == IntPtr.Zero)
{
throw new InvalidOperationException("No wave data is open");
}
int offset = (int) position;
int mmOrigin = WinMMInterop.SEEK_CUR;
if (origin == SeekOrigin.Begin)
{
offset += dataOffset;
mmOrigin = WinMMInterop.SEEK_SET;
}
else if (origin == SeekOrigin.End)
{
mmOrigin = WinMMInterop.SEEK_END;
}
int result = WinMMInterop.mmioSeek(hMmio, offset, mmOrigin);
if (result == -1)
{
throw new WaveStreamException(
String.Format("Failed to seek to position {0} in file",
position));
}
return (long) result;
}
private void OpenWaveFile()
{
CloseWaveFile();
if (!File.Exists(waveFile))
{
throw new FileNotFoundException(
String.Format("The file {0} does not exist", waveFile));
}
hMmio = WinMMInterop.mmioOpen(waveFile, IntPtr.Zero,
WinMMInterop.MMIO_READ);
if (hMmio == IntPtr.Zero)
{
throw new IOException(
String.Format("Could not open file {0}", waveFile));
}
GetWaveData();
}
private void GetWaveData()
{
int result = 0;
WinMMInterop.MMCKINFO mmckInfoParent = new WinMMInterop.MMCKINFO();
// Descend into the wave header:
mmckInfoParent.fccType = WinMMInterop.mmioStringToFOURCC("WAVE", 0);
result = WinMMInterop.mmioDescendParent(hMmio, ref mmckInfoParent, 0,
WinMMInterop.MMIO_FINDRIFF);
if (result != WinMMInterop.MMSYSERR_NOERROR)
{
CloseWaveFile();
throw new WaveStreamException(
String.Format("The file {0} is not a wave file", waveFile));
}
// Descend into the fmt chunk:
WinMMInterop.MMCKINFO mmckSubChunkIn = new WinMMInterop.MMCKINFO();
mmckSubChunkIn.ckid = WinMMInterop.mmioStringToFOURCC("fmt", 0);
result = WinMMInterop.mmioDescend(hMmio, ref mmckSubChunkIn, ref
mmckInfoParent,
WinMMInterop.MMIO_FINDCHUNK);
if (result != WinMMInterop.MMSYSERR_NOERROR)
{
CloseWaveFile();
throw new WaveStreamException(
String.Format("Unable to locate the format chunk in file {0}",
waveFile));
}
format = new WinMMInterop.WAVEFORMATEX();
result = WinMMInterop.mmioReadWaveFormat(hMmio, ref format,
mmckSubChunkIn.ckSize);
if (result == -1)
{
CloseWaveFile();
throw new WaveStreamException(
String.Format("Unable to read the wave format from file {0}",
waveFile));
}
// Find the data subchunk
result = WinMMInterop.mmioAscend(hMmio, ref mmckSubChunkIn, 0);
mmckSubChunkIn.ckid = WinMMInterop.mmioStringToFOURCC("data", 0);
result = WinMMInterop.mmioDescend(hMmio, ref mmckSubChunkIn, ref
mmckInfoParent,
WinMMInterop.MMIO_FINDCHUNK);
if (result != WinMMInterop.MMSYSERR_NOERROR)
{
CloseWaveFile();
throw new WaveStreamException(
String.Format("Unable to locate the data chunk in file {0}",
waveFile));
}
dataOffset = WinMMInterop.mmioSeek(hMmio, 0, WinMMInterop.SEEK_CUR);
audioLength = mmckSubChunkIn.ckSize;
}
private void CloseWaveFile()
{
if (hMmio != IntPtr.Zero)
{
WinMMInterop.mmioClose(hMmio, 0);
hMmio = IntPtr.Zero;
audioLength = 0;
}
}
}
}
|
|||
|
|
||||