/// <summary>
        /// Dump current sound into an Audio object.
        /// </summary>
        /// <param name="name">The name that display on panes.</param>
        /// <param name="saveName">The intended save name.</param>
        /// <param name="matchScriptID">The corresponding script id for the current track.</param>
        /// <returns>An Audio object with all fields set up.</returns>
        public static Audio DumpAudio(string name, string saveName, int length, int matchScriptID)
        {
            // get length from file if length is not specified
            if (length == -1)
            {
                length = GetAudioLength(saveName);  
            }

            var audio = new Audio
                            {
                                Name = name,
                                SaveName = saveName,
                                LengthMillis = length,
                                Length = ConvertMillisToTime(length),
                                MatchScriptID = matchScriptID,
                                Type = GetAudioType(saveName)
                            };
            
            return audio;
        }
        private void MapShapesWithAudio(PowerPointSlide slide)
        {
            var relativeSlideId = GetRelativeSlideIndex(slide.ID);
            XmlParser xmlParser;

            string searchRule = string.Format("^({0}|{1})", SpeechShapePrefixOld, SpeechShapePrefix);
            var shapes = slide.GetShapesWithMediaType(PpMediaType.ppMediaTypeSound, new Regex(searchRule));

            if (shapes.Count == 0)
            {
                return;
            }

            try
            {
                xmlParser = new XmlParser(string.Format(_tempShapAudioXmlFormat, relativeSlideId + 1));
            }
            catch (ArgumentException)
            {
                // xml does not exist, means this page is either a new page or
                // created dues to pasting. For either case we do nothing
                return;
            }

            // iterate through all shapes, skip audios that are not generated speech
            foreach (var shape in shapes)
            {
                var audio = new Audio();

                // detect audio type
                switch (shape.MediaFormat.AudioSamplingRate)
                {
                    case Audio.GeneratedSamplingRate:
                        audio.Type = Audio.AudioType.Auto;
                        break;
                    case Audio.RecordedSamplingRate:
                        audio.Type = Audio.AudioType.Record;
                        break;
                    default:
                        MessageBox.Show(TextCollection.RecorderUnrecognizeAudio);
                        break;
                }

                // derive matched id from shape name
                var temp = shape.Name.Split(new[] { ' ' });
                audio.MatchScriptID = Int32.Parse(temp[2]);

                // get corresponding audio
                audio.Name = shape.Name;
                audio.SaveName = _tempFullPath + xmlParser.GetCorrespondingAudio(audio.Name);
                audio.Length = AudioHelper.GetAudioLengthString(audio.SaveName);
                audio.LengthMillis = AudioHelper.GetAudioLength(audio.SaveName);

                // maintain a sorted audio list
                // Note: here relativeID == slide.Index - 1
                if (audio.MatchScriptID >= _audioList[relativeSlideId].Count)
                {
                    _audioList[relativeSlideId].Add(audio);
                }
                else
                {
                    _audioList[relativeSlideId].Insert(audio.MatchScriptID, audio);
                }

                // match id > total script count -> script does not exsit
                if (audio.MatchScriptID >= _scriptList[relativeSlideId].Count)
                {
                    audio.MatchScriptID = -1;
                }
            }
        }
        public void RecButtonIdleHandler()
        {
            // close unfinished session
            ResetSession();

            // check input device, abort if no input device connected
            if (!NInputDeviceExists())
            {
                MessageBox.Show(TextCollection.RecorderNoInputDeviceMsg, TextCollection.RecorderNoInputDeviceMsgBoxTitle,
                                MessageBoxButtons.OK, MessageBoxIcon.Error);

                return;
            }

            // UI settings
            ResetRecorder();
            statusLabel.Text = TextCollection.RecorderRecordingStatusLabel;
            statusLabel.Visible = true;
            recButton.Image = Properties.Resources.Pause;
            // disable control of playing
            playButton.Enabled = false;
            // enable stop button
            stopButton.Enabled = true;
            // disable control of both lists
            recDisplay.Enabled = false;
            scriptDisplay.Enabled = false;

            // clear the undo buffer
            _undoAudioBuffer = null;

            // track the on going script index if not in slide show mode
            if (_inShowControlBox == null ||
                _inShowControlBox.GetCurrentStatus() == InShowControl.ButtonStatus.Idle)
            {
                // if there's a corresponding script
                if (scriptDisplay.SelectedIndices.Count > 0)
                {
                    _replaceScriptIndex = scriptDisplay.SelectedIndices[0];
                }
                else
                {
                    _replaceScriptIndex = -1;
                }

                _replaceScriptSlide = PowerPointCurrentPresentationInfo.CurrentSlide;
            }

            // change the status to recording status
            _recButtonStatus = RecorderStatus.Recording;

            // new record, clip counter and total length should be reset
            _recordClipCnt = 0;
            _recordTotalLength = 0;
            // construct new save name
            var tempSaveName = String.Format(_tempWaveFileNameFormat, _recordClipCnt);

            // start recording
            NStartRecordAudio(tempSaveName, 11025, 16, 1, true);

            // start the timer
            _timerCnt = 0;
            _timer = new System.Threading.Timer(TimerEvent, null, 0, 1000);
        }
        public void StopButtonRecordingHandler(int scriptIndex, PowerPointSlide currentSlide, bool buffered)
        {
            // enable the control of play button
            playButton.Enabled = true;

            // change rec button status, rec button text, update status label
            // and stop timer
            _recButtonStatus = RecorderStatus.Idle;
            recButton.Image = Properties.Resources.Record;
            statusLabel.Text = TextCollection.RecorderReadyStatusLabel;
            ResetTimer();

            // get current playback, can be null if there's no matched audio
            var currentPlayback = GetPlaybackFromList(scriptIndex, currentSlide.ID);

            try
            {
                // stop recording in the first play to reduce redundant recording
                NStopRecordAudio();

                // adjust the stop time difference between timer-stop and recording-stop
                _recordTotalLength += NGetRecordLengthMillis();
                timerLabel.Text = AudioHelper.ConvertMillisToTime(_recordTotalLength);

                // recorder resources clean up
                NCleanup();

                // ask if the user wants to do the replacement
                var result = DialogResult.Yes;

                // prompt to the user only when escaping the slide show while recording
                if (_inShowControlBox != null &&
                    _inShowControlBox.GetCurrentStatus() == InShowControl.ButtonStatus.Estop)
                {
                    if (currentPlayback == null)
                    {
                        result = MessageBox.Show(TextCollection.RecorderSaveRecordMsg,
                                                 TextCollection.RecorderSaveRecordMsgBoxTitle, MessageBoxButtons.YesNo,
                                                 MessageBoxIcon.Question);
                    }
                    else
                    {
                        result =
                            MessageBox.Show(
                                string.Format(TextCollection.RecorderReplaceRecordMsgFormat, currentPlayback.Name),
                                TextCollection.RecorderReplaceRecordMsgBoxTitle, MessageBoxButtons.YesNo,
                                MessageBoxIcon.Question);
                    }
                }

                if (result == DialogResult.No)
                {
                    // user does not want to save the file, delete all the temp files
                    DeleteTempAudioFiles();
                }
                else
                {
                    // user confirms the recording, save the file and replace the record
                    string saveName;
                    string displayName;
                    Audio newRec;

                    var relativeSlideId = GetRelativeSlideIndex(currentSlide.ID);

                    // map the script index with record index
                    // here a simple iteration will find:
                    // 1. the replacement position if a record exists;
                    // 2. an insertion position if a record needs to be added
                    // specially, index == -1 means the record needs to be appended
                    var recordIndex = -1;

                    if (scriptIndex == -1)
                    {
                        if (recDisplay.SelectedItems.Count > 0)
                        {
                            recordIndex = recDisplay.SelectedIndices[0];
                        }
                    }
                    else
                    {
                        for (int i = 0; i < _audioList[relativeSlideId].Count; i++)
                        {
                            var audio = _audioList[relativeSlideId][i];

                            if (audio.MatchScriptID >= scriptIndex)
                            {
                                recordIndex = i;
                                break;
                            }
                        }
                    }

                    // if current playback != null -> there's a corresponding record for the
                    // script, we can do the replacement;
                    if (currentPlayback != null)
                    {
                        saveName = currentPlayback.SaveName.Replace(".wav", " rec.wav");
                        displayName = currentPlayback.Name;
                        var matchId = currentPlayback.MatchScriptID;

                        if (scriptIndex == -1)
                        {
                            matchId = -1;
                        }

                        newRec = AudioHelper.DumpAudio(displayName, saveName, _recordTotalLength, matchId);

                        // note down the old record and replace the record list
                        _undoAudioBuffer = _audioList[relativeSlideId][recordIndex];
                        _audioList[relativeSlideId][recordIndex] = newRec;

                        // update the item in display
                        // check status of in show control box to:
                        // 1. reduce unnecessary update (won't see the display lists while slide show)
                        // 2. current slide == null during slide show, use in show box status to guard
                        // null ptr exception.
                        if (_inShowControlBox == null ||
                            _inShowControlBox.GetCurrentStatus() != InShowControl.ButtonStatus.Rec &&
                            relativeSlideId == GetRelativeSlideIndex(PowerPointCurrentPresentationInfo.CurrentSlide.ID))
                        {
                            UpdateRecordList(recordIndex, displayName, newRec.Length);
                        }
                    }
                    else
                    // if current playback == null -> there's NO corresponding record for the
                    // script, we need to construct the new record and insert it to a proper
                    // position
                    {
                        var saveNameSuffix = " " + scriptIndex + " rec.wav";
                        saveName = _tempFullPath + String.Format(SaveNameFormat, relativeSlideId) + saveNameSuffix;

                        // the display name -> which script it corresponds to
                        displayName = String.Format(SpeechShapeFormat, scriptIndex);

                        newRec = AudioHelper.DumpAudio(displayName, saveName, _recordTotalLength, scriptIndex);

                        // insert the new audio
                        if (recordIndex == -1)
                        {
                            _audioList[relativeSlideId].Add(newRec);
                            // update record index, will be used in highlighting
                            recordIndex = _audioList[relativeSlideId].Count - 1;
                        }
                        else
                        {
                            _audioList[relativeSlideId].Insert(recordIndex, newRec);
                        }

                        // update the whole record display list if not in slide show mode
                        if (_inShowControlBox == null ||
                            _inShowControlBox.GetCurrentStatus() != InShowControl.ButtonStatus.Rec &&
                            relativeSlideId == GetRelativeSlideIndex(PowerPointCurrentPresentationInfo.CurrentSlide.ID))
                        {
                            UpdateRecordList(relativeSlideId);

                            // highlight the latest added record
                            recDisplay.Items[recordIndex].Selected = true;
                        }
                    }

                    // save current sound -> rename the temp file to the correct save name
                    NMergeAudios(_tempFullPath, "temp", saveName);

                    // update the script list if not in slide show mode
                    if (scriptIndex != -1 && (_inShowControlBox == null ||
                        _inShowControlBox.GetCurrentStatus() != InShowControl.ButtonStatus.Rec &&
                        relativeSlideId == GetRelativeSlideIndex(PowerPointCurrentPresentationInfo.CurrentSlide.ID)))
                    {
                        UpdateScriptList(scriptIndex, null, ScriptStatus.Recorded);
                    }

                    // check if we need to buffer the audio or embed the audio
                    if (!buffered)
                    {
                        newRec.EmbedOnSlide(currentSlide, scriptIndex);

                        if (!Globals.ThisAddIn.Ribbon.RemoveAudioEnabled)
                        {
                            Globals.ThisAddIn.Ribbon.RemoveAudioEnabled = true;
                            Globals.ThisAddIn.Ribbon.RefreshRibbonControl("RemoveAudioButton");
                        }
                    }
                    else
                    {
                        while (AudioBuffer.Count < currentSlide.Index)
                        {
                            AudioBuffer.Add(new List<Tuple<Audio, int>>());
                        }

                        AudioBuffer[currentSlide.Index - 1].Add(new Tuple<Audio, int>(newRec, scriptIndex));
                    }
                }
            }
            catch (Exception e)
            {
                ErrorDialogWrapper.ShowDialog("Record cannot be saved\n",
                                              "Error when saving the file", e);
                throw;
            }
            finally
            // do the following UI re-setup
            {
                // enable control of both lists
                recDisplay.Enabled = true;
                scriptDisplay.Enabled = true;
                // disable stop button
                stopButton.Enabled = false;
            }
        }
        private void RefreshAudioList(PowerPointSlide slide, string[] names)
        {
            var relativeSlideId = GetRelativeSlideIndex(slide.ID);

            while (relativeSlideId >= _audioList.Count)
            {
                _audioList.Add(new List<Audio>());
            }

            _audioList[relativeSlideId].Clear();

            // if audio names have not been given, retrieve from files.
            if (names == null)
            {
                MapShapesWithAudio(slide);
            }
            else
            {
                // construct audio object and put into audio collection
                for (int i = 0; i < names.Length; i++)
                {
                    string saveName = names[i];
                    string name = String.Format(SpeechShapeFormat, i);
                    var audio = new Audio(name, saveName, i);

                    _audioList[relativeSlideId].Add(audio);
                }
            }
        }