protected virtual void OnBufferFilled(BufferFilledEventArgs bfea)
 {
     if (BufferFilled != null)
     {
         BufferFilled(this, bfea);
     }
 }
        /// <summary>
        /// Gets a dictionary that contains the Fast Fourier Transform (FFT) for each data channel.
        /// </summary>
        //public Dictionary<EdkDll.EE_DataChannel_t, double[]> FastFourierTransforms
        //{
        //    get
        //    {
        //        if( this._FFTs == null )
        //        {
        //            this._FFTs = new Dictionary<EdkDll.EE_DataChannel_t, double[]>();
        //        }

        //        return this._FFTs;
        //    }
        //}

        #endregion Public Properties

        ///////////////////////////////////////////////////////////////////////

        #region Public Methods

        /// <summary>
        /// Adds a new data frame to the <see cref="SampleBufferDictionary"/>.
        /// </summary>
        /// <param name="dataFrame">A dictionary that contains the new sample data.</param>
        /// <remarks>
        /// <para>
        /// Access to the <see cref="SampleBufferDictionary"/> is synchronized.
        /// </para>
        /// <para>
        /// The <paramref name="dataFrame"/> parameter is assumed to originate from the
        /// <see cref="EmoEngine"/>, which means that all data channels from the neuroheadset
        /// are present.
        /// </para>
        /// <para>
        /// Only data channels with the "AddToBuffer" <see cref="ChannelContext"/> set
        /// to <c>true</c> are added. Usually, signals that are not electrode data
        /// are excluded, but some signals, such as <see cref="EdkDll.EE_DataChannel_t.COUNTER"/>,
        /// are usually included for diagnostic purposes.
        /// </para>
        /// </remarks>
        public void Add(Dictionary <EdkDll.EE_DataChannel_t, double[]> dataFrame)
        {
            // Return if there is no data frame.
            if (dataFrame != null)
            {
                // Get the number of samples in the new data frame. This number is assumed
                // to be the same for all channels.
                // TBD: picked this channel at random (first in Intellisense list).
                int sampleCount = dataFrame[EdkDll.EE_DataChannel_t.AF3].Length;

                // Track whether the samples in the new data frame will
                // wrap from the end to the beginning of the buffer.
                //bool wrapped = false;

                // Synchronize access to the dictionary.
                lock (this._lockThis)
                {
                    // If this is the first frame, set the buffer size.
                    if (this.Count == 0)
                    {
                        this.BufferSize = sampleCount * this.FrameCapacity;
                    }

                    // Compute the number of samples between the current sample and the end of the buffer.
                    int samplesToBufferEnd = this.BufferSize - this.CurrentSampleIndex;

                    bool wrapped = samplesToBufferEnd < sampleCount ? true : false;

                    // Not enough space, so compute how many samples to wrap to the
                    // start of the buffer.
                    int samplesToWrap = sampleCount - samplesToBufferEnd;

                    var wrapDictionary = new Dictionary <EdkDll.EE_DataChannel_t, double[]>();

                    // Iterate through all of the channels in the data frame and copy each array of new
                    // samples to the corresponding buffer.
                    foreach (KeyValuePair <EdkDll.EE_DataChannel_t, double[]> kvp in dataFrame)
                    {
                        bool addToBuffer = (bool)EmoEngineClient.ChannelContexts[kvp.Key]["AddToBuffer"];
                        if (addToBuffer)
                        {
                            Emotiv.EdkDll.EE_DataChannel_t channel = kvp.Key;

                            // If the dictionary entry for the channel doesn't exist, create it.
                            if (!this.ContainsKey(channel))
                            {
                                // Create a dictionary entry for the channel.
                                this[channel] = null;
                            }

                            // If the dictionary entry for the channel average value doesn't exist, create it.
                            if (!this.ChannelAverages.ContainsKey(channel))
                            {
                                // Create a dictionary entry for the channel average.
                                this.ChannelAverages[channel] = 0;
                            }

                            // Get the new samples in the data frame.
                            double[] newSamples = kvp.Value;

                            // Compute the average of the new samples.
                            double newSampleAverage = newSamples.Sum() / newSamples.Length;

                            // Determine whether to remove DC bias from this channel.
                            bool removeDCBias = (bool)EmoEngineClient.ChannelContexts[channel]["RemoveDCBias"];

                            // Determine whether to compute the FFT for the channel.
                            bool computeFFT = (bool)EmoEngineClient.ChannelContexts[channel]["ComputeFFT"];

                            // Get or create the buffer for the current channel.
                            double[] currentBuffer = this[channel];
                            if (currentBuffer == null)
                            {
                                currentBuffer = new double[this.BufferSize];
                                this[channel] = currentBuffer;
                            }

                            ///////////////////////////////////////

                            #region Remove DC bias from new samples

                            // Compute the running average for the current channel.
                            this.ChannelAverages[kvp.Key] = (this.TotalFrames * this.ChannelAverages[kvp.Key] + newSampleAverage) / (this.TotalFrames + 1);

                            // Remove the DC bias, if the channel context specifies it.
                            if (removeDCBias)
                            {
                                // Subtract the average value for the channel.
                                var newSamplesMinusDCBias = newSamples.Select(d => d - this.ChannelAverages[kvp.Key]);
                                newSamples = newSamplesMinusDCBias.ToArray();
                            }

                            // Special case for the COUNTER channel.
                            if (kvp.Key == EdkDll.EE_DataChannel_t.COUNTER)
                            {
                                // Subtract the median value for the channel.
                                var newSamplesMinusDCBias = newSamples.Select(d => d - 64.0d);
                                newSamples = newSamplesMinusDCBias.ToArray();
                            }

                            #endregion Remove DC bias from new samples

                            ////////////////////////////////////////

                            #region Commit new samples to the buffer

                            // Compute the number of samples between the current sample and the end of the buffer.
                            // int samplesToBufferEnd = this.BufferSize - this.CurrentSampleIndex;

                            // If there is enough space, copy the new samples into the buffer.
                            //if( samplesToBufferEnd >= newSamples.Length )
                            if (!wrapped)
                            {
                                newSamples.CopyTo(currentBuffer, this.CurrentSampleIndex);
                            }
                            else
                            {
                                // Not enough space, so compute how many samples to wrap to the
                                // start of the buffer.
                                //int samplesToWrap = newSamples.Length - samplesToBufferEnd;

                                double[] wrap = new double[newSamples.Length];
                                Array.Copy(
                                    newSamples,
                                    samplesToBufferEnd,
                                    wrap,
                                    0,
                                    samplesToWrap);

                                wrapDictionary.Add(channel, wrap);

                                // Copy new samples to the end of the buffer.
                                //Array.Copy(
                                //    newSamples,
                                //    0,
                                //    currentBuffer,
                                //    this.CurrentSampleIndex,
                                //    samplesToBufferEnd );

                                //var buffer = currentBuffer.Select( d => d );
                                //double[] bufferD = buffer.ToArray<double>();
                                //BufferFilledEventArgs bfea = new BufferFilledEventArgs( bufferD, channel );
                                //this.OnBufferFilled( bfea );

                                // Copy the wrapped new samples to the beginning of the buffer.
                                //Array.Copy(
                                //    newSamples,
                                //    samplesToBufferEnd,
                                //    currentBuffer,
                                //    0,
                                //    samplesToWrap );

                                // Indicate that the buffer wrapped.
                                //wrapped = true;
                            }

                            #endregion Commit new samples to the buffer

                            ////////////////////////////////////////////

                            #region Compute Fast Fourier Transform (FFT)

                            // Optionally compute the FFT for the channel.
                            // TBD: lock?
                            //if( computeFFT )
                            //{
                            //    this.FastFourierTransforms[kvp.Key] = ComputeRealFourierTransform( kvp.Key );
                            //}

                            #endregion Compute Fast Fourier Transform (FFT)
                        }
                    }

                    if (wrapped)
                    {
                        BufferFilledEventArgs bfea = new BufferFilledEventArgs(this);
                        this.OnBufferFilled(bfea);

                        foreach (KeyValuePair <EdkDll.EE_DataChannel_t, double[]> kvp in wrapDictionary)
                        {
                            // Copy the wrapped new samples to the beginning of the buffer.
                            Array.Copy(
                                kvp.Value,
                                0,
                                this[kvp.Key],
                                this.CurrentSampleIndex,
                                samplesToBufferEnd);
                        }
                    }

                    // Increment the count of total frames.
                    this.TotalFrames++;

                    // Increment the current frame index, modulo the buffer capacity (in frames).
                    this.CurrentFrameIndex = (this.CurrentFrameIndex + 1) % this.FrameCapacity;

                    // Increment the current sample index, modulo the buffer capacity (in samples).
                    this.CurrentSampleIndex = (this.CurrentSampleIndex + sampleCount) % this.BufferSize;
                }
            }
        }