Click here to Skip to main content
15,891,372 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 45K   1.7K   24  
Application allows voice communications (VOIP) utilizing the Distributed Interactive Simulation protocol (IEEE 1278.1)
// Copyright (c) 2010, Peter Smith
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, 
// are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list 
// of conditions and the following disclaimer. 
//
// Redistributions in binary form must reproduce the above copyright notice, this 
// list of conditions and the following disclaimer in the documentation and/or 
// other materials provided with the distribution. 
//
// Neither the name of the  Author nor the names of its contributors may be used to 
// endorse or promote products derived from this software without specific prior 
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE.

namespace AudioIN
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;

    using OpenTK;
    using OpenTK.Audio;
    using OpenTK.Audio.OpenAL;

    public class Microphone
    {
        #region Fields

        /// <summary>
        /// Boolean value indicating if microphone intialized correctly
        /// </summary>
        public bool isMicrophoneValid = false;

        AudioCapture audio_capture;
        private byte[] buffer = new byte[1024];
        private bool continuePolling = false;
        private Queue<byte[]> microphoneData = new Queue<byte[]>();
        Thread pollMicrophone = null;
        private int SampleToByte = 2;
        private int src;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initialize the microphone
        /// </summary>
        /// <param name="samplingRate">Sample rate used during capture</param>
        /// <param name="gain">Gain of the microphone</param>
        /// <param name="deviceCaptureName">Name of the Device used for capturing audio</param>
        /// <param name="format">One of the openAL formats</param>
        /// <param name="bufferSize">Size of the buffer</param>
        public Microphone(int samplingRate, float gain, string deviceCaptureName, ALFormat format, int bufferSize)
        {
            isMicrophoneValid = SetUpMicrophone(samplingRate, gain, deviceCaptureName, format, bufferSize);
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Queue used to hold the incoming data from the microphone.
        /// </summary>
        public Queue<byte[]> MicrophoneData
        {
            get
            {
                return microphoneData;
            }

            set
            {
                microphoneData = value;
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Start recording from the Microphone
        /// </summary>
        public void StartRecording()
        {
            if (isMicrophoneValid == true)
            {
                audio_capture.Start();
                continuePolling = true;

                if (pollMicrophone == null ||pollMicrophone.IsAlive == false)
                {
                    pollMicrophone = new Thread(PollMicrophoneForData);
                    pollMicrophone.Start();
                }
            }
        }

        /// <summary>
        /// Stop recording from the Microphone
        /// </summary>
        public void StopRecording()
        {
            if (isMicrophoneValid == true)
            {
                continuePolling = false;
                audio_capture.Stop();
                ClearBuffers(0);
                MicrophoneData.Clear();
            }
        }

        /// <summary>
        /// Clears the Microphone buffers
        /// </summary>
        /// <param name="input">Which buffer</param>
        private void ClearBuffers(int input)
        {
            int[] freedbuffers;
            if (input == 0)
            {
                int BuffersProcessed;
                AL.GetSource(src, ALGetSourcei.BuffersProcessed, out BuffersProcessed);
                if (BuffersProcessed == 0)
                    return;
                freedbuffers = AL.SourceUnqueueBuffers(src, BuffersProcessed);
            }
            else
            {
                freedbuffers = AL.SourceUnqueueBuffers(src, input);
            }
            AL.DeleteBuffers(freedbuffers);
        }

        private bool InitializeMicrophone(int samplingRate, float gain, string deviceCaptureName, ALFormat format, int bufferSize)
        {
            AL.Listener(ALListenerf.Gain, gain);

            src = AL.GenSource();

            SampleToByte = NumberOfBytesPerSample(format);

            buffer = new byte[bufferSize];

            try
            {
                audio_capture = new AudioCapture(deviceCaptureName, samplingRate, format, bufferSize);
            }
            catch (AudioDeviceException ade)
            {
                audio_capture = null;
            }

            if (audio_capture == null)
                return false;

            return true;
        }

        private int NumberOfBytesPerSample(ALFormat format)
        {
            int bytesPerSample = 2;

            switch (format)
            {
                case ALFormat.Mono16:
                    bytesPerSample = 2;
                    break;
                case ALFormat.Mono8:
                    bytesPerSample = 1;
                    break;
                case ALFormat.MonoALawExt:
                    bytesPerSample = 1;
                    break;
                case ALFormat.MonoDoubleExt:
                    bytesPerSample = 8;
                    break;
                case ALFormat.MonoFloat32Ext:
                    bytesPerSample = 4;
                    break;
                case ALFormat.MonoIma4Ext:
                    bytesPerSample = 4;
                    break;
                case ALFormat.MonoMuLawExt:
                    bytesPerSample = 1;
                    break;
                case ALFormat.Mp3Ext:
                    bytesPerSample = 2; //Guessed might not be correct
                    break;
                case ALFormat.Multi51Chn16Ext:
                    bytesPerSample = 6 * 2;
                    break;
                case ALFormat.Multi51Chn32Ext:
                    bytesPerSample = 6 * 4;
                    break;
                case ALFormat.Multi51Chn8Ext:
                    bytesPerSample = 6 * 1;
                    break;
                case ALFormat.Multi61Chn16Ext:
                    bytesPerSample = 7 * 2;
                    break;
                case ALFormat.Multi61Chn32Ext:
                    bytesPerSample = 7 * 4;
                    break;
                case ALFormat.Multi61Chn8Ext:
                    bytesPerSample = 7 * 1;
                    break;
                case ALFormat.Multi71Chn16Ext:
                    bytesPerSample = 7 * 2;
                    break;
                case ALFormat.Multi71Chn32Ext:
                    bytesPerSample = 7 * 4;
                    break;
                case ALFormat.Multi71Chn8Ext:
                    bytesPerSample = 7 * 1;
                    break;
                case ALFormat.MultiQuad16Ext:
                    bytesPerSample = 4 * 2;
                    break;
                case ALFormat.MultiQuad32Ext:
                    bytesPerSample = 4 * 4;
                    break;
                case ALFormat.MultiQuad8Ext:
                    bytesPerSample = 4 * 1;
                    break;
                case ALFormat.MultiRear16Ext:
                    bytesPerSample = 1 * 2;
                    break;
                case ALFormat.MultiRear32Ext:
                    bytesPerSample =  1 * 4;
                    break;
                case ALFormat.MultiRear8Ext:
                    bytesPerSample = 1 * 1;
                    break;
                case ALFormat.Stereo16:
                    bytesPerSample = 2 * 2;
                    break;
                case ALFormat.Stereo8:
                    bytesPerSample = 2 * 1;
                    break;
                case ALFormat.StereoALawExt:
                    bytesPerSample = 2 * 1;
                    break;
                case ALFormat.StereoDoubleExt:
                    bytesPerSample = 2 * 8;
                    break;
                case ALFormat.StereoFloat32Ext:
                    bytesPerSample = 2 * 4;
                    break;
                case ALFormat.StereoIma4Ext:
                    bytesPerSample = 1; //Guessed
                    break;
                case ALFormat.StereoMuLawExt:
                    bytesPerSample = 2 * 1;
                    break;
                case ALFormat.VorbisExt:
                    bytesPerSample = 2; //Guessed
                    break;
                default:
                    break;
            }

            return bytesPerSample;
        }

        /// <summary>
        /// Used to poll the Microphone for data
        /// </summary>
        private void PollMicrophoneForData()
        {
            while (continuePolling)
            {
                Thread.Sleep(1); //Allow GUI some time

                if (audio_capture.AvailableSamples * SampleToByte >= buffer.Length)
                {
                    UpdateSamples();
                }
            }
        }

        private bool SetUpMicrophone(int samplingRate, float gain, string deviceCaptureName, ALFormat format, int bufferSize)
        {
            //return InitializeMicrophone(8000, 4.0f, AudioCapture.DefaultDevice, ALFormat.Mono16, 1024, codec);
            return InitializeMicrophone(samplingRate, gain, deviceCaptureName, format, bufferSize);
        }

        private void UpdateSamples()
        {
            buffer = new byte[buffer.Length];
            audio_capture.ReadSamples(buffer, buffer.Length / SampleToByte); //Need to divide as the readsamples expects the value to be in 2 bytes.

            //Queue raw data, let receiving application determine if it needs to compress
            this.microphoneData.Enqueue(buffer);
            ClearBuffers(0);
        }

        #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