WaveFile.cs
//**********************************************************
//* Direct X Sound Recorder *
//* Author: D. Zouchinski *
//* http://zouchinski.co.uk *
//* Copyright @ 2009-2011 *
//**********************************************************
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.DirectX.DirectSound;
namespace SoundRecorder
{
public class WaveFile : IDisposable
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MMCKINFO
{
public int ckid;
public int cksize;
public int fccType;
public int dwDataOffset;
public int dwFlags;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public short wFormatTag;
public short nChannels;
public int nSamplesPerSec;
public int nAvgBytesPerSec;
public short nBlockAlign;
public short wBitsPerSample;
public short cbSize;
}
[DllImport("winmm.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr mmioOpen([MarshalAs(UnmanagedType.LPWStr)] string file, IntPtr Null, int dwFlags);
[DllImport("winmm.dll")]
private static extern int mmioCreateChunk(IntPtr hFile, ref MMCKINFO ciWave, int dwFlags);
[DllImport("winmm.dll")]
private static extern int mmioAscend(IntPtr hFile, ref MMCKINFO ciWave, int dwFlags);
[DllImport("winmm.dll")]
private static extern int mmioWrite(IntPtr hFile, ref WAVEFORMATEX fmt, int size);
[DllImport("winmm.dll")]
private static extern int mmioWrite(IntPtr hFile, [MarshalAs(UnmanagedType.LPArray)] short[] data, int size);
[DllImport("winmm.dll")]
private static extern int mmioWrite(IntPtr hFile, [MarshalAs(UnmanagedType.LPArray)] int[] data, int size);
[DllImport("winmm.dll")]
private static extern int mmioSeek(IntPtr hFile, int pos, int flags);
[DllImport("winmm.dll")]
private static extern int mmioClose(IntPtr hFile, int flags);
private static int mmioFOURCC(int ch0, int ch1, int ch2, int ch3)
{
return ((int)(byte)(ch0) | ((int)(byte)(ch1) << 8) | ((int)(byte)(ch2) << 16) | ((int)(byte)(ch3) << 24));
}
private IntPtr hFile;
private WAVEFORMATEX fmt = new WAVEFORMATEX();
private MMCKINFO ciWave = new MMCKINFO();
private MMCKINFO ciFmt = new MMCKINFO();
private MMCKINFO ciFact = new MMCKINFO();
private MMCKINFO ciData = new MMCKINFO();
private int dwTotalSamples;
private int factPos;
public WaveFile(string file, WaveFormat f)
{
fmt.cbSize = 0;
fmt.nChannels = f.Channels;
fmt.wFormatTag = (short)f.FormatTag;
fmt.nSamplesPerSec = f.SamplesPerSecond;
fmt.wBitsPerSample = f.BitsPerSample;
fmt.nBlockAlign = f.BlockAlign;
fmt.nAvgBytesPerSec = f.AverageBytesPerSecond;
const int SEEK_CUR = 1;
const int MMIO_CREATE = 4096;
const int MMIO_WRITE = 1;
const int MMIO_ALLOCBUF = 65536;
const int MMIO_CREATERIFF = 32;
hFile = mmioOpen(file, IntPtr.Zero, MMIO_CREATE | MMIO_WRITE | MMIO_ALLOCBUF);
if (hFile != IntPtr.Zero)
{
ciWave.fccType = mmioFOURCC('W', 'A', 'V', 'E');
mmioCreateChunk(hFile, ref ciWave, MMIO_CREATERIFF);
ciFmt.ckid = mmioFOURCC('f', 'm', 't', ' ');
mmioCreateChunk(hFile, ref ciFmt, 0);
mmioWrite(hFile, ref fmt, Marshal.SizeOf(typeof(WAVEFORMATEX)));
mmioAscend(hFile, ref ciFmt, 0);
ciFact.ckid = mmioFOURCC('f', 'a', 'c', 't');
ciFact.cksize = 4;
mmioCreateChunk(hFile, ref ciFact, 0);
factPos = mmioSeek(hFile, 0, SEEK_CUR);
dwTotalSamples = 0;
int[] samples = new int[] { dwTotalSamples };
mmioWrite(hFile, samples, 4);
mmioAscend(hFile, ref ciFact, 0);
ciData.ckid = mmioFOURCC('d', 'a', 't', 'a');
mmioCreateChunk(hFile, ref ciData, 0);
}
}
public bool Write(short[] data)
{
if (hFile == IntPtr.Zero)
return false;
if (data == null)
return false;
if (data.Length == 0)
return false;
mmioWrite(hFile, data, data.Length * 2);
dwTotalSamples += data.Length / fmt.nChannels;
return true;
}
public double TimeInSeconds()
{
return (double)dwTotalSamples / (double)fmt.nSamplesPerSec;
}
public bool Close()
{
if (hFile == IntPtr.Zero)
return false;
const int SEEK_SET = 0;
const int SEEK_CUR = 1;
// out of data and wave chanks
mmioAscend(hFile, ref ciData, 0);
mmioAscend(hFile, ref ciWave, 0);
// Update fact
int curPos = mmioSeek(hFile, 0, SEEK_CUR);
mmioSeek(hFile, factPos, SEEK_SET);
int[] samples = new int[] { dwTotalSamples };
mmioWrite(hFile, samples, 4);
mmioSeek(hFile, curPos, SEEK_SET);
// close file
mmioClose(hFile, 0);
hFile = IntPtr.Zero;
return true;
}
#region IDisposable Members
public void Dispose()
{
Close();
}
#endregion
}
}