Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / C#

SampleTagger, fast indexer for Universal Patch Finder

Rate me:
Please Sign up or sign in to vote.
4.20/5 (2 votes)
29 May 2013CPOL3 min read 16.1K   222   5  
How to index REX, RCY, RX2, AIFF, WAV audio files in Universal Patch Finder.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace SampleTagger.Parsers
{
    /*
     * This class provide all primitive to read a RIFF file
     * in big endian or not.
     */
    public abstract class BinaryFileReader
    {
        protected FileStream BinaryFile;
        protected BinaryReader Reader;
        protected SampleFileInfo info;

        /*
         * Here the main entry point. we ask to parse a specific filename 
         */
        public SampleFileInfo Parse(string filename)
        {
            info = new SampleFileInfo();
            BinaryFile = new FileStream(filename, FileMode.Open, FileAccess.Read);
            Reader = new BinaryReader(BinaryFile);
            ReadContent();
            Reader.Close();
            BinaryFile.Close();
            return info;
        }

        /*
         * this method is called by Parse() and must be implemented
         * in a subclass
         */
        abstract protected void ReadContent();

        /*
         * Use this to convert a bunch of bytes in a struct 
         */
        protected T ByteArrayToStructure<T>(byte[] bytes) where T : struct
        {
            GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
            T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),
                typeof(T));
            handle.Free();
            return stuff;
        }

        /*
         * return or set the stream position 
         */
        protected long Position
        {
            get
            {
                return Reader.BaseStream.Position;
            }
            set
            {
                Reader.BaseStream.Position = value;
            }
        }
        protected byte[] ReadBytes(int count)
        {
            return Reader.ReadBytes(count);
        }

        protected long IndianSwap(long value)
        {
            return
                    (((value >> 56) & 0xFF) << 00) |
                    (((value >> 48) & 0xFF) << 08) |
                    (((value >> 40) & 0xFF) << 16) |
                    (((value >> 32) & 0xFF) << 24) |

                    (((value >> 24) & 0xFF) << 32) |
                    (((value >> 16) & 0xFF) << 40) |
                    (((value >> 08) & 0xFF) << 48) |
                    (((value >> 00) & 0xFF) << 56);

        }
        protected int IndianSwap(int value)
        {
            return (((value >> 24) & 0xFF) << 00) |
                                    (((value >> 16) & 0xFF) << 08) |
                                    (((value >> 08) & 0xFF) << 16) |
                                    (((value >> 00) & 0xFF) << 24);

        }
        protected int IndianSwap(short value)
        {
            int lvalue = (int)value;
            return
                    (((lvalue >> 08) & 0xFF) << 00) |
                    (((lvalue >> 00) & 0xFF) << 08);
        }
        protected int BigIndianReadInt32()
        {
            return IndianSwap(Reader.ReadInt32());
        }
        protected int ReadInt8()
        {
            return (int)Reader.ReadByte();
        }
        protected int ReadInt32()
        {
            return Reader.ReadInt32();
        }
        protected int BigIndianReadInt16()
        {
            return IndianSwap(Reader.ReadInt16());
        }
        protected int ReadInt16()
        {
            return Reader.ReadInt16();
        }
        protected long ReadInt64()
        {
            return IndianSwap(Reader.ReadInt64());
        }
        protected float ReadSingle()
        {
            byte[] data = ReadBytes(4);
            return BitConverter.ToSingle(data, 0);
        }
        /*
         * read the ID of a RIFF Chunk
         * an ID is always 4 char long
         * 
         * null is returned if we reach the end of stream or somthing is wrong
         */
        protected String ReadID()
        {
            int c1;

            // some buggy chunk terminate with 0
            // so we skip it to start properly on the next chunk id
            for (; ; )
            {
                if (Reader.BaseStream.Position == Reader.BaseStream.Length)
                    return null; // EOF
                c1 = (int)Reader.ReadByte();
                if (c1 == -1)
                    return null;
                if (c1 >= ' ' && c1 <= 127)
                    break;
            }
            char[] chars = new char[4];
            chars[0] = (char)c1;
            chars[1] = Reader.ReadChar();
            chars[2] = Reader.ReadChar();
            chars[3] = Reader.ReadChar();
            return new String(chars);
        }
        /*
         * use this to read the samplefrequency in a AIFF file 
         */
        protected double ReadLongDouble()
        {
            byte[] b = Reader.ReadBytes(10);
            return LongDouble.ToDouble(b, true);
        }
        protected String BytesToString(byte[] data)
        {
            String value = Encoding.ASCII.GetString(data).Trim();
            int idx = value.IndexOf((char)0);
            if (idx != -1)
                return value.Substring(0, idx).Trim();
            else
                return value.Trim();
        }
    }
    /*
     * http://www.codeproject.com/KB/cs/longdouble.aspx
     */
    class LongDouble
    {
        private static byte[] IndianSwap(byte[] data)
        {
            int size = data.Length;
            byte[] swap = new byte[size];
            for (int i = 0; i < 10; i++)
            {
                swap[i] = data[size - 1 - i];
            }
            return swap;
        }
        public static double ToDouble(byte[] Value, bool BigIndian)
        {
            if (Value == null)
                throw new ArgumentNullException("Value");

            if (Value.Length != 10)
                throw new ArgumentException("Combination of Value length and StartIndex was not large enough.");

            if (BigIndian)
            {
                Value = IndianSwap(Value);
            }
            //extract fields
            byte s = (byte)(Value[9] & 0x80);
            short e = (short)(((Value[9] & 0x7F) << 8) | Value[8]);
            byte j = (byte)(Value[7] & 0x80);
            long f = Value[7] & 0x7F;
            for (sbyte i = 6; i >= 0; i--)
            {
                f <<= 8;
                f |= Value[i];
            }

            if (e == 0) //subnormal, pseudo-denormal or zero
                return 0;

            if (j == 0)
                throw new NotSupportedException();

            if (e == 0x7FFF) //+infinity, -infinity or nan
            {
                if (f != 0)
                    return double.NaN;
                if (s == 0)
                    return double.PositiveInfinity;
                else
                    return double.NegativeInfinity;
            }

            //translate f
            f >>= 11;

            //translate e
            e -= (0x3FFF - 0x3FF);

            if (e >= 0x7FF) //outside the range of a double
                throw new OverflowException();
            else if (e < -51) //too small to translate into subnormal
                return 0;
            else if (e < 0) //too small for normal but big enough to represent as subnormal
            {
                f |= 0x10000000000000;
                f >>= (1 - e);
                e = 0;
            }

            byte[] newBytes = System.BitConverter.GetBytes(f);

            newBytes[7] = (byte)(s | (e >> 4));
            newBytes[6] = (byte)(((e & 0x0F) << 4) | newBytes[6]);

            return System.BitConverter.ToDouble(newBytes, 0);
        }
        public static byte[] GetBytes(double Value, bool BigIndian)
        {
            byte[] oldBytes = System.BitConverter.GetBytes(Value);

            //extract fields
            byte s = (byte)(oldBytes[7] & 0x80);
            short e = (short)(((oldBytes[7] & 0x7F) << 4) | ((oldBytes[6] & 0xF0) >> 4));
            byte j = 0x80;
            long f = oldBytes[6] & 0xF;
            for (sbyte i = 5; i >= 0; i--)
            {
                f <<= 8;
                f |= oldBytes[i];
            }

            //translate f
            f <<= 11;

            if (e == 0x7FF) //+infinity, -infinity or nan
                e = 0x7FFF;
            else if (e == 0 && f == 0) //zero
                j = 0;
            else //normal or subnormal
            {
                if (e == 0) //subnormal
                {
                    f <<= 1;
                    while (f > 0)
                    {
                        e--;
                        f <<= 1;
                    }
                    f &= long.MaxValue;
                }

                e += (0x3FFF - 0x3FF); //translate e
            }

            byte[] newBytes = new byte[10];
            System.BitConverter.GetBytes(f).CopyTo(newBytes, 0);

            newBytes[9] = (byte)(s | (e >> 8));
            newBytes[8] = (byte)(e & 0xFF);
            newBytes[7] = (byte)(j | newBytes[7]);

            if (BigIndian)
            {
                newBytes = IndianSwap(newBytes);
            }
            return newBytes;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions