vbAccelerator - Contents of code file: AudioCDWriterCS\frmAudioCDCreator.cs

This file is part of the download Audio CD Writer, which is described in the article Writing Audio CDs.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using vbAccelerator.Components.ImapiWrapper;
using vbAccelerator.Audio.WaveStream;

namespace AudioCDWriter
{
   /// <summary>
   /// Summary description for frmAudioCDCreator.
   /// </summary>
   public class frmAudioCDCreator : System.Windows.Forms.Form
   {
      private System.Windows.Forms.Label lblRecorder;
      private System.Windows.Forms.ComboBox cboRecorder;
      private System.Windows.Forms.Label lblFiles;
      private System.Windows.Forms.ListBox lstFiles;
      private System.Windows.Forms.Button btnMoveUp;
      private System.Windows.Forms.Button btnMoveDown;
      private System.Windows.Forms.Button btnAdd;
      private System.Windows.Forms.Button btnRemove;
      private System.Windows.Forms.Button btnBurn;
      private System.Windows.Forms.CheckBox chkSimulate;
      private System.Windows.Forms.ProgressBar prgBurn;
      private System.Windows.Forms.StatusBar sbrMain;
      private System.Windows.Forms.CheckBox chkEjectWhenComplete;
      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;

      private DiscMaster discMaster = null;
      private RedbookDiscMaster redbookDiscMaster = null;
      private AudioCDCreator creator = null;
      private CreateCDDelegate createCDHandler = null;
      private bool burnInProgress = false;
      private bool cancel = false;
      private bool preparingFlag = false;
      private int lastCompleted = 0;

      public frmAudioCDCreator()
      {
         Application.ThreadException += new
          ThreadExceptionEventHandler(application_ThreadException);

         //
         // Required for Windows Form Designer support
         //
         InitializeComponent();

         Show();
         Refresh();

         // Create the disc master object and initialse the recorder list:
         GetRecorders();

         // Attach all of the events:
         discMaster.AddProgress += new
          ProgressEventHandler(discMaster_AddProgress);
         discMaster.BlockProgress += new
          ProgressEventHandler(discMaster_BlockProgress);
         discMaster.BurnComplete += new
          CompletionStatusEventHandler(discMaster_BurnComplete);
         discMaster.ClosingDisc += new
          EstimatedTimeOperationEventHandler(discMaster_ClosingDisc);
         discMaster.EraseComplete += new
          CompletionStatusEventHandler(discMaster_EraseComplete);
         discMaster.PnPActivity += new EventHandler(discMaster_PnPActivity);
         discMaster.PreparingBurn += new
          EstimatedTimeOperationEventHandler(discMaster_PreparingBurn);
         discMaster.QueryCancel += new
          QueryCancelEventHandler(discMaster_QueryCancel);
         discMaster.TrackProgress += new
          ProgressEventHandler(discMaster_TrackProgress);

      }

      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
         if( disposing )
         {
            if (components != null) 
            {
               components.Dispose();
            }
            redbookDiscMaster.Dispose();
            discMaster.Dispose();
         }
         base.Dispose( disposing );
      }

      #region Windows Form Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         System.Resources.ResourceManager resources = new
          System.Resources.ResourceManager(typeof(frmAudioCDCreator));
         this.lblRecorder = new System.Windows.Forms.Label();
         this.cboRecorder = new System.Windows.Forms.ComboBox();
         this.lblFiles = new System.Windows.Forms.Label();
         this.lstFiles = new System.Windows.Forms.ListBox();
         this.btnMoveUp = new System.Windows.Forms.Button();
         this.btnMoveDown = new System.Windows.Forms.Button();
         this.btnAdd = new System.Windows.Forms.Button();
         this.btnRemove = new System.Windows.Forms.Button();
         this.btnBurn = new System.Windows.Forms.Button();
         this.chkSimulate = new System.Windows.Forms.CheckBox();
         this.chkEjectWhenComplete = new System.Windows.Forms.CheckBox();
         this.prgBurn = new System.Windows.Forms.ProgressBar();
         this.sbrMain = new System.Windows.Forms.StatusBar();
         this.SuspendLayout();
         // 
         // lblRecorder
         // 
         this.lblRecorder.Font = new System.Drawing.Font("Tahoma", 8.25F,
          System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
          ((System.Byte)(0)));
         this.lblRecorder.Location = new System.Drawing.Point(8, 8);
         this.lblRecorder.Name = "lblRecorder";
         this.lblRecorder.Size = new System.Drawing.Size(76, 16);
         this.lblRecorder.TabIndex = 0;
         this.lblRecorder.Text = "&Recorder:";
         // 
         // cboRecorder
         // 
         this.cboRecorder.DropDownStyle =
          System.Windows.Forms.ComboBoxStyle.DropDownList;
         this.cboRecorder.Location = new System.Drawing.Point(92, 4);
         this.cboRecorder.Name = "cboRecorder";
         this.cboRecorder.Size = new System.Drawing.Size(404, 21);
         this.cboRecorder.TabIndex = 1;
         this.cboRecorder.SelectedIndexChanged += new
          System.EventHandler(this.cboRecorder_SelectedIndexChanged);
         // 
         // lblFiles
         // 
         this.lblFiles.Font = new System.Drawing.Font("Tahoma", 8.25F,
          System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
          ((System.Byte)(0)));
         this.lblFiles.Location = new System.Drawing.Point(8, 40);
         this.lblFiles.Name = "lblFiles";
         this.lblFiles.Size = new System.Drawing.Size(76, 16);
         this.lblFiles.TabIndex = 2;
         this.lblFiles.Text = "&Files:";
         // 
         // lstFiles
         // 
         this.lstFiles.Location = new System.Drawing.Point(92, 36);
         this.lstFiles.Name = "lstFiles";
         this.lstFiles.SelectionMode =
          System.Windows.Forms.SelectionMode.MultiExtended;
         this.lstFiles.Size = new System.Drawing.Size(380, 251);
         this.lstFiles.TabIndex = 3;
         // 
         // btnMoveUp
         // 
         this.btnMoveUp.FlatStyle = System.Windows.Forms.FlatStyle.System;
         this.btnMoveUp.Font = new System.Drawing.Font("Wingdings", 8.25F,
          System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
          ((System.Byte)(2)));
         this.btnMoveUp.Location = new System.Drawing.Point(476, 128);
         this.btnMoveUp.Name = "btnMoveUp";
         this.btnMoveUp.Size = new System.Drawing.Size(20, 20);
         this.btnMoveUp.TabIndex = 4;
         this.btnMoveUp.Text = "";
         this.btnMoveUp.Click += new System.EventHandler(this.btnMoveUp_Click);
         // 
         // btnMoveDown
         // 
         this.btnMoveDown.FlatStyle = System.Windows.Forms.FlatStyle.System;
         this.btnMoveDown.Font = new System.Drawing.Font("Wingdings", 8.25F,
          System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
          ((System.Byte)(2)));
         this.btnMoveDown.Location = new System.Drawing.Point(476, 152);
         this.btnMoveDown.Name = "btnMoveDown";
         this.btnMoveDown.Size = new System.Drawing.Size(20, 20);
         this.btnMoveDown.TabIndex = 5;
         this.btnMoveDown.Text = "";
         this.btnMoveDown.Click += new
          System.EventHandler(this.btnMoveDown_Click);
         // 
         // btnAdd
         // 
         this.btnAdd.FlatStyle = System.Windows.Forms.FlatStyle.System;
         this.btnAdd.Location = new System.Drawing.Point(96, 292);
         this.btnAdd.Name = "btnAdd";
         this.btnAdd.Size = new System.Drawing.Size(88, 24);
         this.btnAdd.TabIndex = 6;
         this.btnAdd.Text = "&Add...";
         this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
         // 
         // btnRemove
         // 
         this.btnRemove.FlatStyle = System.Windows.Forms.FlatStyle.System;
         this.btnRemove.Location = new System.Drawing.Point(188, 292);
         this.btnRemove.Name = "btnRemove";
         this.btnRemove.Size = new System.Drawing.Size(88, 24);
         this.btnRemove.TabIndex = 7;
         this.btnRemove.Text = "&Remove...";
         this.btnRemove.Click += new System.EventHandler(this.btnRemove_Click);
         // 
         // btnBurn
         // 
         this.btnBurn.FlatStyle = System.Windows.Forms.FlatStyle.System;
         this.btnBurn.Location = new System.Drawing.Point(96, 336);
         this.btnBurn.Name = "btnBurn";
         this.btnBurn.Size = new System.Drawing.Size(88, 24);
         this.btnBurn.TabIndex = 8;
         this.btnBurn.Text = "&Burn";
         this.btnBurn.Click += new System.EventHandler(this.btnBurn_Click);
         // 
         // chkSimulate
         // 
         this.chkSimulate.Checked = true;
         this.chkSimulate.CheckState = System.Windows.Forms.CheckState.Checked;
         this.chkSimulate.FlatStyle = System.Windows.Forms.FlatStyle.System;
         this.chkSimulate.Location = new System.Drawing.Point(188, 336);
         this.chkSimulate.Name = "chkSimulate";
         this.chkSimulate.Size = new System.Drawing.Size(236, 16);
         this.chkSimulate.TabIndex = 9;
         this.chkSimulate.Text = "&Simulate";
         // 
         // chkEjectWhenComplete
         // 
         this.chkEjectWhenComplete.Checked = true;
         this.chkEjectWhenComplete.CheckState =
          System.Windows.Forms.CheckState.Checked;
         this.chkEjectWhenComplete.FlatStyle =
          System.Windows.Forms.FlatStyle.System;
         this.chkEjectWhenComplete.Location = new System.Drawing.Point(188,
          356);
         this.chkEjectWhenComplete.Name = "chkEjectWhenComplete";
         this.chkEjectWhenComplete.Size = new System.Drawing.Size(236, 16);
         this.chkEjectWhenComplete.TabIndex = 10;
         this.chkEjectWhenComplete.Text = "&Eject When Complete";
         // 
         // prgBurn
         // 
         this.prgBurn.Location = new System.Drawing.Point(96, 388);
         this.prgBurn.Name = "prgBurn";
         this.prgBurn.Size = new System.Drawing.Size(400, 20);
         this.prgBurn.TabIndex = 11;
         // 
         // sbrMain
         // 
         this.sbrMain.Location = new System.Drawing.Point(0, 418);
         this.sbrMain.Name = "sbrMain";
         this.sbrMain.Size = new System.Drawing.Size(500, 20);
         this.sbrMain.TabIndex = 12;
         // 
         // frmAudioCDCreator
         // 
         this.AutoScaleBaseSize = new System.Drawing.Size(5, 14);
         this.ClientSize = new System.Drawing.Size(500, 438);
         this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                        this.sbrMain,
                                                        this.prgBurn,
                                                       
                                         this.chkEjectWhenComplete,
                                                        this.chkSimulate,
                                                        this.btnBurn,
                                                        this.btnRemove,
                                                        this.btnAdd,
                                                        this.btnMoveDown,
                                                        this.btnMoveUp,
                                                        this.lstFiles,
                                                        this.lblFiles,
                                                        this.cboRecorder,
                                                        this.lblRecorder});
         this.Font = new System.Drawing.Font("Tahoma", 8.25F,
          System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
          ((System.Byte)(0)));
         this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
         this.Name = "frmAudioCDCreator";
         this.Text = "vbAccelerator .NET Audio CD Creator";
         this.ResumeLayout(false);

      }
      #endregion

      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main() 
      {
         Application.Run(new frmAudioCDCreator());         
      }

      private void application_ThreadException(object sender,
       ThreadExceptionEventArgs e)
      {
         MessageBox.Show(this, String.Format("An untrapped exception occurred:
          {0}.  The application will now close.", e.Exception),
            Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
         Close();
      }

      protected override void OnClosing(CancelEventArgs e)
      {   
         // Don't close if burn in progress
         if (burnInProgress)
         {
            e.Cancel = true;

            MessageBox.Show(this, "A CD burn is currently in progress.  Cancel
             the burn before closing this application.", 
               Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
         }
         
         // Clear up:
         base.OnClosing(e);
         Cursor.Current = Cursors.WaitCursor;
         sbrMain.Text = "Closing IMAPI interface...";
         redbookDiscMaster.Dispose();
         discMaster.Dispose();
         Cursor.Current = Cursors.Default;
      }

      private void GetRecorders()
      {
         try
         {
            discMaster = new DiscMaster();
            redbookDiscMaster = discMaster.RedbookDiscMaster();

            foreach (DiscRecorder recorder in discMaster.DiscRecorders)
            {
               cboRecorder.Items.Add(
                  new ComboRecorderWrapper(recorder));            
            }

            if (cboRecorder.Items.Count > 0)
            {
               cboRecorder.SelectedIndex = 0;
            }
         }
         catch (Exception ex)
         {
            MessageBox.Show(this, String.Format("Unable to initialise the IMAPI
             library {0}", ex),
               Text, MessageBoxButtons.OK, MessageBoxIcon.Stop);
            if (discMaster != null)
            {
               discMaster.Dispose();
            }
         }
      }

      private void btnAdd_Click(object sender, System.EventArgs e)
      {
         OpenFileDialog filePicker = new OpenFileDialog();
         filePicker.Filter = "Wave Files (*.WAV)|*.WAV|All Files (*.*)|*.*";
         filePicker.DefaultExt = "WAV";
         filePicker.Multiselect = true;
         if (filePicker.ShowDialog(this) == DialogResult.OK)
         {
            foreach (string file in filePicker.FileNames)
            {
               // Confirm that this is a 16 bit, stereo, 44.1kHz Wave:
               WaveStreamReader reader = new WaveStreamReader(file);
               if ((reader.Channels == 2) && (reader.BitsPerSample == 16) && 
                  (reader.SamplingFrequency == 44100))
               {
                  // Add to the ListBox
                  FileWrapper wrapper = new FileWrapper(file, (int)
                   reader.Length);
                  if (!lstFiles.Items.Contains(wrapper))
                  {
                     lstFiles.Items.Add(wrapper);
                  }
                  else
                  {
                     MessageBox.Show(this, String.Format("The file {0} has
                      already been added.", file),
                        Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
                  }
               }
               else
               {
                  MessageBox.Show(this, String.Format("The file {0} is not a
                   suitable wave file.  Only 16 bit stereo 44.1kHz PCM files
                   can be added.", file),
                     Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
               }
            }
         }
      }

      private void btnRemove_Click(object sender, System.EventArgs e)
      {
         if (lstFiles.SelectedIndices.Count > 0)
         {
            object[] selected = new object[lstFiles.SelectedIndices.Count];
            lstFiles.SelectedItems.CopyTo(selected, 0);
            foreach (object obj in selected)
            {
               lstFiles.Items.Remove(obj);
            }
         }
      }

      private void btnMoveUp_Click(object sender, System.EventArgs e)
      {
         if (lstFiles.SelectedIndices.Count > 0)
         {
            int[] selected = new int[lstFiles.SelectedIndices.Count];
            lstFiles.SelectedIndices.CopyTo(selected, 0);

            for (int i = 0; i < selected.Length; i++)
            {
               if (selected[i] > 0)
               {
                  object swap = lstFiles.Items[selected[i] - 1];
                  lstFiles.Items[selected[i] - 1] = lstFiles.Items[selected[i]];
                  lstFiles.Items[selected[i]] = swap;
                  lstFiles.SetSelected(selected[i] - 1, true);
               }
            }
         }         
      }

      private void btnMoveDown_Click(object sender, System.EventArgs e)
      {
         if (lstFiles.SelectedIndices.Count > 0)
         {
            int[] selected = new int[lstFiles.SelectedIndices.Count];
            lstFiles.SelectedIndices.CopyTo(selected, 0);

            for (int i = 0; i < selected.Length; i++)
            {
               if (selected[i] < (lstFiles.Items.Count - 1))
               {
                  object swap = lstFiles.Items[selected[i] + 1];
                  lstFiles.Items[selected[i] + 1] = lstFiles.Items[selected[i]];
                  lstFiles.Items[selected[i]] = swap;
                  lstFiles.SetSelected(selected[i] + 1, true);
               }
            }
         }
      }

      private void btnBurn_Click(object sender, System.EventArgs e)
      {
         if (burnInProgress)
         {
            cancel = true;
         }
         else
         {
            BurnCd(chkSimulate.Checked, chkEjectWhenComplete.Checked);
         }
      }

      /// <summary>
      /// Sets the application's mode
      /// </summary>
      /// <param name="burning"><c>true</c> if a burn is in progress</param>
      private void SetApplicationMode(bool burning)
      {
         if (burning)
         {
            burnInProgress = true;
            cancel = false;
            btnBurn.Text = "Cancel";

            btnMoveUp.Enabled = false;
            btnMoveDown.Enabled = false;
            btnAdd.Enabled = false;
            btnRemove.Enabled = false;
            lstFiles.Enabled = false;
            cboRecorder.Enabled = false;
         }
         else
         {
            burnInProgress = false;
            btnBurn.Text = "&Burn";

            btnMoveUp.Enabled = true;
            btnMoveDown.Enabled = true;
            btnAdd.Enabled = true;
            btnRemove.Enabled = true;
            lstFiles.Enabled = true;
            cboRecorder.Enabled = true;
         }
      }

      /// <summary>
      /// Checks the media in the drive.
      /// </summary>
      /// <returns></returns>
      private bool CheckMedia()
      {
         bool mediaOk = false;

         sbrMain.Text = "Checking media in recorder...";
         Cursor.Current = Cursors.WaitCursor;
         DiscRecorder recorder = ((ComboRecorderWrapper)
          cboRecorder.SelectedItem).DiscRecorder;
         recorder.OpenExclusive();
         MediaDetails media = recorder.GetMediaDetails();
         recorder.CloseExclusive();
         Cursor.Current = Cursors.Default;
         if (media.MediaPresent)
         {
            if (
               ((media.MediaFlags & MEDIA_FLAGS.MEDIA_BLANK) ==
                MEDIA_FLAGS.MEDIA_BLANK) 
               && ((media.MediaFlags & MEDIA_FLAGS.MEDIA_WRITABLE) ==
                MEDIA_FLAGS.MEDIA_WRITABLE)
               )
            {
               sbrMain.Text = "Preparing to burn CD.";
               mediaOk = true;
            }
            else
            {
               sbrMain.Text = "Media cannot be written to.";
               MessageBox.Show(this, 
                  String.Format("The disc in drive {0} ({1}) is either not
                   blank or cannot be written to.", recorder.DriveLetter,
                   recorder.PnPID),
                  Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
               
            }
         }
         else
         {
            sbrMain.Text = "No media in drive.";
            MessageBox.Show(this, 
               String.Format("Please insert blank, writable media into drive
                {0} ({1})", recorder.DriveLetter, recorder.PnPID),
               Text, MessageBoxButtons.OK, MessageBoxIcon.Information);

         }
         return mediaOk;
      }

      /// <summary>
      /// Burns the CD asynchronously
      /// </summary>
      /// <param name="simulate"><c>true</c> to simulate, <c>false</c> to
       really burn</param>
      /// <param name="ejectWhenComplete"><c>true</c> if the tray should be
       ejected once
      /// burn has finished.</param>
      private void BurnCd(bool simulate, bool ejectWhenComplete)
      {
         // Check that we have media:
         if (CheckMedia())
         {
            // Determine number of progress chunks:
            InitialiseProgress();

            // Asynchronously invoke the creator
            creator = new AudioCDCreator(discMaster, redbookDiscMaster);
            foreach (FileWrapper wrapper in lstFiles.Items)
            {
               creator.AddFile(wrapper.FileName);
            }
            
            createCDHandler = new CreateCDDelegate(creator.CreateCD);
            AsyncCallback callback = new AsyncCallback(createCD_Complete);
            IAsyncResult ar = createCDHandler.BeginInvoke(simulate,
             ejectWhenComplete, callback, null);

            SetApplicationMode(true);
         }
      }

      private void InitialiseProgress()
      {
         int totalBlocks = 0;
         foreach (FileWrapper file in lstFiles.Items)
         {
            // Add blocks twice; once for staging, once for burning
            totalBlocks += (file.AudioBytes / redbookDiscMaster.AudioBlockSize)
             * 2;
         }
         // Add 2 x 100 for preparing burn and closing disc 
         totalBlocks += 200;
         prgBurn.Maximum = totalBlocks;
      }

      /// <summary>
      /// Called when CD creation completes
      /// </summary>
      /// <param name="ar">Result of method call (none)</param>
      private void createCD_Complete(IAsyncResult ar)
      {
         SetApplicationMode(false);
         try
         {
            createCDHandler.EndInvoke(ar);
         }
         catch (Exception ex)
         {
            MessageBox.Show(this, String.Format("CD creation failed: Exception:
             {0}", ex,
               Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation));
         }
      }

      /// <summary>
      /// Raised whilst blocks are added to the stash
      /// </summary>
      /// <param name="sender">Disc master object</param>
      /// <param name="e">Progess information</param>
      private void discMaster_AddProgress(object sender, ProgressEventArgs e)
      {
         sbrMain.Text = String.Format("Adding {0} of {1}", e.Completed,
          e.Total);
         if (e.Completed < lastCompleted)
         {
            lastCompleted = 0;
         }
         prgBurn.Value += (e.Completed - lastCompleted);
         lastCompleted = e.Completed;
      }

      /// <summary>
      /// Raised whilst blocks are burnt to the disc
      /// </summary>
      /// <param name="sender">Disc master object</param>
      /// <param name="e">Progress information</param>
      private void discMaster_BlockProgress(object sender, ProgressEventArgs e)
      {
         sbrMain.Text = String.Format("Block {0} of {1}", e.Completed, e.Total);
         if (preparingFlag)
         {
            prgBurn.Value += 100;
            preparingFlag = false;
            lastCompleted = 0;
         }
         if (e.Completed < lastCompleted)
         {
            lastCompleted = 0;
         }
         prgBurn.Value += (e.Completed - lastCompleted);
         lastCompleted = e.Completed;
      }

      /// <summary>
      /// Raised when CD burning has completed
      /// </summary>
      /// <param name="sender">Disc Master object</param>
      /// <param name="e">Status information</param>
      private void discMaster_BurnComplete(object sender,
       CompletionStatusEventArgs e)
      {         
         prgBurn.Value += 100;
         sbrMain.Text = String.Format("Burn Complete!");
      }

      /// <summary>
      /// Raised as the disc is about to be closed
      /// </summary>
      /// <param name="sender">Disc Master object</param>
      /// <param name="e">Estimated time to close the disc</param>
      private void discMaster_ClosingDisc(object sender,
       EstimatedTimeOperationEventArgs e)
      {
         sbrMain.Text = String.Format("Closing disc (estimated time {0}s)...", 
            e.EstimatedSeconds);
      }

      /// <summary>
      /// Raised when an erase operation completes
      /// </summary>
      /// <param name="sender">Disc master object</param>
      /// <param name="e">Status information</param>
      private void discMaster_EraseComplete(object sender,
       CompletionStatusEventArgs e)
      {
         sbrMain.Text = String.Format("Erase Complete!");
      }

      /// <summary>
      /// Raised when a recorder is changed
      /// </summary>
      /// <param name="sender">Disc master object</param>
      /// <param name="e">Not used</param>
      private void discMaster_PnPActivity(object sender, EventArgs e)
      {
         //
      }

      /// <summary>
      /// Raised before a disc is about to be burnt.
      /// </summary>
      /// <param name="sender">Disc Master object</param>
      /// <param name="e">Estimated time before burning begins</param>
      private void discMaster_PreparingBurn(object sender,
       EstimatedTimeOperationEventArgs e)
      {
         sbrMain.Text = String.Format("Preparing to burn disc (estimated time
          {0}s)...", 
            e.EstimatedSeconds);
         preparingFlag = true;
      }

      /// <summary>
      /// Raised to request whether to cancel a stash or burn operation
      /// </summary>
      /// <param name="sender">Disc Master object</param>
      /// <param name="e">Whether to cancel or not</param>
      private void discMaster_QueryCancel(object sender, QueryCancelEventArgs e)
      {
         e.Cancel = cancel;
      }

      /// <summary>
      /// Raised as tracks are burnt to disc
      /// </summary>
      /// <param name="sender">Disc Master object</param>
      /// <param name="e">Track progress</param>
      private void discMaster_TrackProgress(object sender, ProgressEventArgs e)
      {
         if (e.Completed < e.Total)
         {
            sbrMain.Text = String.Format("Track {0} of {1}", e.Completed + 1,
             e.Total);
         }
         else
         {
            sbrMain.Text = String.Format("Completed adding last track");
         }
      }

      /// <summary>
      /// Set the active disc recorder
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void cboRecorder_SelectedIndexChanged(object sender,
       System.EventArgs e)
      {
         if (cboRecorder.SelectedIndex > -1)
         {
            ComboRecorderWrapper wrapper = (ComboRecorderWrapper)
             cboRecorder.SelectedItem;
            discMaster.DiscRecorders.ActiveDiscRecorder = wrapper.DiscRecorder;
         }
      }


      /// <summary>
      /// Slight class to wrap a file for purposes of associating it
      /// with a list box
      /// </summary>
      class FileWrapper
      {
         private readonly string fileName;
         private readonly int audioBytes;

         public FileWrapper(string fileName, int audioBytes)
         {
            this.fileName = fileName;
            this.audioBytes = audioBytes;
         }

         public int AudioBytes
         {
            get
            {
               return audioBytes;
            }
         }

         public string FileName
         {
            get
            {
               return fileName;
            }
         }

         public override int GetHashCode()
         {
            return fileName.GetHashCode();
         }

         public override bool Equals(object obj)
         {
            bool equals = false;
            FileWrapper other = (FileWrapper) obj;
            if (other.FileName.Equals(fileName))
            {
               equals = true;
            }
            return equals;
         }

         public override string ToString()
         {
            return this.fileName;
         }
      }


      /// <summary>
      /// Slight class to wrap a DiscRecorder allowing it to be associated
      /// with a combo box.
      /// </summary>
      class ComboRecorderWrapper
      {
         private readonly DiscRecorder recorder;

         public ComboRecorderWrapper(DiscRecorder recorder)
         {
            this.recorder = recorder;
         }

         public DiscRecorder DiscRecorder
         {
            get
            {
               return recorder;
            }
         }

         public override string ToString()
         {
            return String.Format("{0} ({1} {2} {3})", 
               recorder.DriveLetter, recorder.Vendor, recorder.Product,
                recorder.Revision);
         }
      }

   }
   
}