Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#

Using OpenTK/OpenAL to Develop Cross Platform DIS VOIP Application

Rate me:
Please Sign up or sign in to vote.
4.79/5 (8 votes)
15 Mar 2010BSD10 min read 44.6K   1.7K   24  
Application allows voice communications (VOIP) utilizing the Distributed Interactive Simulation protocol (IEEE 1278.1)
namespace LumiSoft.Net.Media.Codec.Audio
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    /// <summary>
    /// Implements PCMA(G711 alaw) codec.
    /// </summary>
    public class PCMA : AudioCodec
    {
        #region Fields

        private static readonly byte[] ALawCompressTable = new byte[]{ 
            1,1,2,2,3,3,3,3,
            4,4,4,4,4,4,4,4,
            5,5,5,5,5,5,5,5,
            5,5,5,5,5,5,5,5,
            6,6,6,6,6,6,6,6,
            6,6,6,6,6,6,6,6,
            6,6,6,6,6,6,6,6,
            6,6,6,6,6,6,6,6,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7,
            7,7,7,7,7,7,7,7
        };
        private static readonly short[] ALawDecompressTable = new short[]{ 
            -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
            -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
            -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
            -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
            -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
            -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
            -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
            -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
            -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
            -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
            -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,
            -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
            -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
            -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
            -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
            -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,
            5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
            7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
            2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
            3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
            22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
            30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
            11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,
            15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
            344,   328,   376,   360,   280,   264,   312,   296,
            472,   456,   504,   488,   408,   392,   440,   424,
            88,    72,   120,   104,    24,     8,    56,    40,
            216,   200,   248,   232,   152,   136,   184,   168,
            1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
            1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
            688,   656,   752,   720,   560,   528,   624,   592,
            944,   912,  1008,   976,   816,   784,   880,   848
        };

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public PCMA()
        {
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets number of bits per sample.
        /// </summary>
        public override int BitsPerSample
        {
            get{ return 8; }
        }

        /// <summary>
        /// Gets codec name.
        /// </summary>
        public override string Name
        {
            get{ return "PCMA"; }
        }

        /// <summary>
        /// Gets sample number of samples in second(hz). 
        /// </summary>
        public override int SampleRate
        {
            get{ return 8000; }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Decodes 8-bit alaw to linear 16-bit PCM.
        /// </summary>
        /// <param name="buffer">Data to decode. Data must be in Little-Endian format.</param>
        /// <param name="offset">Offset in the buffer.</param>
        /// <param name="count">Number of bytes to decode.</param>
        /// <returns>Returns decoded data.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public override byte[] Decode(byte[] buffer,int offset,int count)
        {
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            if(offset < 0 || offset > buffer.Length){
                throw new ArgumentException("Argument 'offse't is out of range.");
            }
            if(count < 1 || (count + offset) > buffer.Length){
                throw new ArgumentException("Argument 'count' is out of range.");
            }

            int    offsetInRetVal = 0;
            byte[] retVal         = new byte[count * 2];
            for(int i=offset;i<buffer.Length;i++){
                short pcm = ALawDecompressTable[buffer[i]];
                retVal[offsetInRetVal++] = (byte)(pcm      & 0xFF);
                retVal[offsetInRetVal++] = (byte)(pcm >> 8 & 0xFF);
            }

            return retVal;
        }

        /// <summary>
        /// Encodes linear 16-bit linear PCM to 8-bit alaw.
        /// </summary>
        /// <param name="buffer">Data to encode. Data must be in Little-Endian format.</param>
        /// <param name="offset">Offset in the buffer.</param>
        /// <param name="count">Number of bytes to encode.</param>
        /// <returns>Returns encoded block.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public override byte[] Encode(byte[] buffer,int offset,int count)
        {
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            if(offset < 0 || offset > buffer.Length){
                throw new ArgumentException("Argument 'offset' is out of range.");
            }
            if(count < 1 || (count + offset) > buffer.Length){
                throw new ArgumentException("Argument 'count' is out of range.");
            }
            if((count % 2) != 0){
                throw new ArgumentException("Invalid 'count' value, it doesn't contain 16-bit boundaries.");
            }

            int    offsetInRetVal = 0;
            byte[] retVal         = new byte[count / 2];
            while(offsetInRetVal < retVal.Length){
                // Little-Endian - lower byte,higer byte.
                short pcm = (short)(buffer[offset + 1] << 8 | buffer[offset]);
                offset += 2;

                retVal[offsetInRetVal++] = LinearToALawSample(pcm);
            }

            return retVal;
        }

        private static byte LinearToALawSample(short sample)
        {
            int  sign           = 0;
            int  exponent       = 0;
            int  mantissa       = 0;
            byte compressedByte = 0;

            sign = ((~sample) >> 8) & 0x80;
            if(sign == 0){
                sample = (short)-sample;
            }
            if(sample > 32635){
                sample = 32635;
            }
            if(sample >= 256){
                exponent = (int)ALawCompressTable[(sample >> 8) & 0x7F];
                mantissa = (sample >> (exponent + 3) ) & 0x0F;
                compressedByte = (byte)((exponent << 4) | mantissa);
            }
            else{
                compressedByte = (byte)(sample >> 4);
            }

            compressedByte ^= (byte)(sign ^ 0x55);

            return compressedByte;
        }

        #endregion Methods
    }
}

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 BSD License


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

Comments and Discussions