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

    }

}