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; } } }