private void LoadEvents()
        {
            if (this.events == null)
            {
                KStudioSeekableEventStream seekableStream = this.stream as KStudioSeekableEventStream;
                if (seekableStream != null)
                {
                    IReadOnlyList <KStudioEventHeader> eventHeaders = seekableStream.EventHeaders;

                    this.events = new EventData[eventHeaders.Count];

                    bool doFrameNumber = false;

                    if (seekableStream.TagSize >= sizeof(uint))
                    {
                        // At this time, assume if there is at least 4 bytes of tag data that it is a frame number
                        doFrameNumber = true;
                    }

                    int count = eventHeaders.Count;
                    if (count > 0)
                    {
                        ulong tick;
                        uint  eventIndex;

                        {
                            KStudioEventHeader eventHeader = eventHeaders[0];
                            tick       = (ulong)eventHeader.RelativeTime.Ticks * EventStreamState.cTimeSpanTicksToTimelineTicks;
                            eventIndex = eventHeader.EventIndex;
                        }

                        int lastIndex = count - 1;

                        for (int i = 0; i < lastIndex; ++i)
                        {
                            KStudioEventHeader eventHeader = eventHeaders[i + 1];
                            ulong nextTick    = (ulong)eventHeader.RelativeTime.Ticks * EventStreamState.cTimeSpanTicksToTimelineTicks;
                            uint? frameNumber = null;

                            if (doFrameNumber)
                            {
                                uint   bufferSize;
                                IntPtr bufferPtr;
                                eventHeader.AccessUnderlyingTagDataBuffer(out bufferSize, out bufferPtr);

                                Debug.Assert(bufferSize >= sizeof(uint));
                                unsafe
                                {
                                    frameNumber = *((uint *)bufferPtr.ToPointer());
                                }
                            }

                            EventData eventDataNode = new EventData((int)eventIndex, frameNumber, tick, nextTick - tick);
                            tick       = nextTick;
                            eventIndex = eventHeader.EventIndex;

                            this.events[i] = eventDataNode;
                        }

                        {
                            KStudioEventHeader eventHeader = eventHeaders[lastIndex];
                            uint?frameNumber = null;

                            if (doFrameNumber)
                            {
                                uint   bufferSize;
                                IntPtr bufferPtr;
                                eventHeader.AccessUnderlyingTagDataBuffer(out bufferSize, out bufferPtr);

                                Debug.Assert(bufferSize >= sizeof(uint));
                                unsafe
                                {
                                    frameNumber = *((uint *)bufferPtr.ToPointer());
                                }
                            }

                            ulong lastDuration;

                            if (this.duration == 0)
                            {
                                lastDuration = EventStreamState.cLastEventDuration;
                            }
                            else
                            {
                                lastDuration = this.duration - tick;
                            }

                            EventData eventDataNode = new EventData((int)eventIndex, frameNumber, tick, lastDuration);

                            this.events[lastIndex] = eventDataNode;
                        }

#if TODODEB
                        EventHandler handler = this.TimeRangeChanged;
                        if (handler != null)
                        {
                            handler(this, EventArgs.Empty);
                        }
                        handler = this.RenderInvalidated;
                        if (handler != null)
                        {
                            handler(this, EventArgs.Empty);
                        }
#endif
                    }
                }

                this.readOnlyEvents = Array.AsReadOnly(this.events);
            }
        }
        public override void Extract(string outputPath)
        {
            KStudioSeekableEventStream         stream  = (KStudioSeekableEventStream)_stream;
            IReadOnlyList <KStudioEventHeader> headers = stream.EventHeaders;

            int frameCount = (int)stream.EventCount;

            double[]  audioTiming    = new double[frameCount];
            const int eventFrameSize = 14432; // Size for a single audio event frame
            const int frameSize      = 1024;  // The size of the actual audio data in an event frame
            int       dataSize       = 0;
            int       bytePos        = 0;

            // DEBUG
            int                missedFrames   = 0;
            double             cumulativeTime = 0;
            double             lDiff          = 0;
            KStudioEventHeader lHeader        = null;

            // END DEBUG

            // Hacky counting of total data size
            for (int i = 0; i < frameCount; i++)
            {
                KStudioEventHeader header = headers[i];
                int    j          = eventFrameSize;
                double missedTime = 0;
                double diff       = header.RelativeTime.TotalMilliseconds;

                /* DEBUG */
                if (lHeader != null)
                {
                    diff = header.RelativeTime.TotalMilliseconds - lHeader.RelativeTime.TotalMilliseconds;
                    if (diff > lDiff)
                    {
                        lDiff = diff;
                    }
                }

                lHeader = header;
                /* END DEBUG */

                do
                {
                    dataSize   += frameSize;
                    j          += eventFrameSize - 16;
                    missedTime += 16;
                } while (j <= (int)header.EventDataSize);

                missedTime      = diff - missedTime;
                cumulativeTime += missedTime;
                while (cumulativeTime > 16)
                {
                    cumulativeTime -= 16;
                    missedFrames++;
                }
            }

            // Calculate additional bytes needed
            //int numBytes = (int) totalMissedTime / 16 * 1024;
            int numBytes = missedFrames * frameSize;

            if (numBytes > 0)
            {
                dataSize += numBytes;
            }

            // DEBUG
            missedFrames   = 0;
            cumulativeTime = 0;
            KStudioEvent lEvent = null;

            // END DEBUG

            byte[] rawAudio = new byte[dataSize];

            for (uint i = 0; i < frameCount; i++)
            {
                int    j          = eventFrameSize;
                var    currEvent  = stream.ReadEvent(i);
                byte[] rawBuffer  = new byte[currEvent.EventDataSize];
                int    offset     = 96;
                double diff       = currEvent.RelativeTime.TotalMilliseconds;
                double missedTime = 0;

                if (lEvent != null)
                {
                    diff = currEvent.RelativeTime.TotalMilliseconds - lEvent.RelativeTime.TotalMilliseconds;
                    if (diff > lDiff)
                    {
                        lDiff = diff;
                    }
                }

                lEvent = currEvent;

                audioTiming[i] = currEvent.RelativeTime.TotalMilliseconds;
                currEvent.CopyEventDataToArray(rawBuffer, 0);

                do
                {
                    Array.Copy(rawBuffer, offset, rawAudio, bytePos, frameSize);
                    bytePos    += frameSize;
                    j          += eventFrameSize - 16;
                    offset     += eventFrameSize - 16;
                    missedTime += 16;
                } while (j <= (int)currEvent.EventDataSize);

                // If there is any missed time, add it here.
                missedTime      = diff - missedTime;
                cumulativeTime += missedTime;
                while (cumulativeTime > 16)
                {
                    cumulativeTime -= 16;
                    missedFrames++;
                    bytePos += frameSize;
                }

                // Update progress
                OnProgressUpdated(new KinectFileProgressChangedEventArgs {
                    Progress   = (int)((float)(i + 1) / frameCount * 100),
                    StatusName = Name
                });
            }

            string filepath = outputPath + "/Kinect_Output";

            Directory.CreateDirectory(filepath);
            File.WriteAllBytes(filepath + "/raw_audio.wav", rawAudio);

            // Write timings
            Utils.WriteTimingToFile(filepath + "/audio_timing.txt", audioTiming);
        }