home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / Managed / DirectSound / CaptureSound / Main.cs < prev    next >
Encoding:
Text File  |  2004-09-27  |  16.2 KB  |  480 lines

  1. //----------------------------------------------------------------------------
  2. // File: Main.cs
  3. //
  4. // Copyright (c) Microsoft Corp. All rights reserved.
  5. //-----------------------------------------------------------------------------
  6. using System;
  7. using System.IO;
  8. using System.Drawing;
  9. using System.Windows.Forms;
  10. using System.Threading;
  11. using Microsoft.DirectX;
  12. using Microsoft.DirectX.DirectSound;
  13.  
  14. public class MainForm : Form
  15. {
  16.     private Label labelMainInputformatText;
  17.     private CheckBox checkboxRecord;
  18.     private Button buttonSoundfile;
  19.     private Label labelFilename;
  20.     private Label labelStatic;
  21.     
  22.     public BufferPositionNotify[] PositionNotify = new BufferPositionNotify[NumberRecordNotifications + 1];  
  23.     public const int NumberRecordNotifications    = 16;
  24.     public AutoResetEvent NotificationEvent    = null;
  25.     public CaptureBuffer applicationBuffer = null;
  26.     public Guid CaptureDeviceGuid = Guid.Empty;
  27.     public Capture applicationDevice = null;
  28.     private string FileName = string.Empty;
  29.     public Notify applicationNotify = null;
  30.     private Thread NotifyThread = null;
  31.     private FileStream WaveFile = null;
  32.     private BinaryWriter Writer = null;
  33.     private string Path = string.Empty;
  34.     public int CaptureBufferSize = 0;
  35.     public int NextCaptureOffset = 0;
  36.     private bool Recording = false;
  37.     public WaveFormat InputFormat;
  38.     private int SampleCount = 0;
  39.     public int NotifySize = 0;
  40.     private bool Capturing = false;
  41.  
  42.     public static void Main()
  43.     {
  44.         try
  45.         {
  46.             using(MainForm form = new MainForm())
  47.             {
  48.                 form.ShowDialog();
  49.             }
  50.         }
  51.         catch{}
  52.     }
  53.     
  54.     private void MainForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
  55.     {
  56.         if (null != NotificationEvent)
  57.         {
  58.             Capturing = false;
  59.             NotificationEvent.Set();
  60.         }
  61.         if (null != applicationBuffer)
  62.             if (applicationBuffer.Capturing)
  63.                 StartOrStopRecord(false);
  64.     }
  65.  
  66.     public MainForm()
  67.     {
  68.         //
  69.         // Required for Windows Form Designer support
  70.         //
  71.         InitializeComponent();
  72.         
  73.         DevicesForm devices = new DevicesForm(this);
  74.         devices.ShowDialog(this);
  75.         InitDirectSound();
  76.         if (null == applicationDevice)
  77.             Close();
  78.         else
  79.         {
  80.             FormatsForm    formats = new FormatsForm(this);
  81.             if (formats.ShowDialog(this) == DialogResult.OK)
  82.             {
  83.         
  84.                 labelMainInputformatText.Text = string.Format("{0} Hz, {1}-bit ", 
  85.                     InputFormat.SamplesPerSecond, 
  86.                     InputFormat.BitsPerSample) +
  87.                     ((1 == InputFormat.Channels) ? "Mono" : "Stereo");
  88.  
  89.                 CreateCaptureBuffer();
  90.             }
  91.             else
  92.             {
  93.                 this.Close();
  94.             }
  95.         }
  96.     }
  97.     #region InitializeComponent code
  98.     private void InitializeComponent()
  99.     {
  100.         this.labelStatic = new System.Windows.Forms.Label();
  101.         this.labelMainInputformatText = new System.Windows.Forms.Label();
  102.         this.checkboxRecord = new System.Windows.Forms.CheckBox();
  103.         this.buttonSoundfile = new System.Windows.Forms.Button();
  104.         this.labelFilename = new System.Windows.Forms.Label();
  105.         this.SuspendLayout();
  106.         // 
  107.         // labelStatic
  108.         // 
  109.         this.labelStatic.Location = new System.Drawing.Point(10, 46);
  110.         this.labelStatic.Name = "labelStatic";
  111.         this.labelStatic.Size = new System.Drawing.Size(75, 13);
  112.         this.labelStatic.TabIndex = 0;
  113.         this.labelStatic.Text = "Input Format:";
  114.         // 
  115.         // labelMainInputformatText
  116.         // 
  117.         this.labelMainInputformatText.Location = new System.Drawing.Point(90, 46);
  118.         this.labelMainInputformatText.Name = "labelMainInputformatText";
  119.         this.labelMainInputformatText.Size = new System.Drawing.Size(262, 13);
  120.         this.labelMainInputformatText.TabIndex = 1;
  121.         // 
  122.         // checkboxRecord
  123.         // 
  124.         this.checkboxRecord.Appearance = System.Windows.Forms.Appearance.Button;
  125.         this.checkboxRecord.Enabled = false;
  126.         this.checkboxRecord.FlatStyle = System.Windows.Forms.FlatStyle.System;
  127.         this.checkboxRecord.Location = new System.Drawing.Point(369, 40);
  128.         this.checkboxRecord.Name = "checkboxRecord";
  129.         this.checkboxRecord.Size = new System.Drawing.Size(75, 23);
  130.         this.checkboxRecord.TabIndex = 2;
  131.         this.checkboxRecord.Text = "&Record";
  132.         this.checkboxRecord.CheckedChanged += new System.EventHandler(this.checkboxRecord_CheckedChanged);
  133.         // 
  134.         // buttonSoundfile
  135.         // 
  136.         this.buttonSoundfile.Location = new System.Drawing.Point(10, 11);
  137.         this.buttonSoundfile.Name = "buttonSoundfile";
  138.         this.buttonSoundfile.Size = new System.Drawing.Size(78, 21);
  139.         this.buttonSoundfile.TabIndex = 3;
  140.         this.buttonSoundfile.Text = "Sound &file...";
  141.         this.buttonSoundfile.Click += new System.EventHandler(this.buttonSoundfile_Click);
  142.         // 
  143.         // labelFilename
  144.         // 
  145.         this.labelFilename.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
  146.         this.labelFilename.Location = new System.Drawing.Point(96, 11);
  147.         this.labelFilename.Name = "labelFilename";
  148.         this.labelFilename.Size = new System.Drawing.Size(348, 21);
  149.         this.labelFilename.TabIndex = 4;
  150.         this.labelFilename.Text = "No file loaded.";
  151.         this.labelFilename.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
  152.         // 
  153.         // MainForm
  154.         // 
  155.         this.AcceptButton = this.buttonSoundfile;
  156.         this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
  157.         this.ClientSize = new System.Drawing.Size(454, 77);
  158.         this.Controls.AddRange(new System.Windows.Forms.Control[] {
  159.                                                                       this.labelStatic,
  160.                                                                       this.labelMainInputformatText,
  161.                                                                       this.checkboxRecord,
  162.                                                                       this.buttonSoundfile,
  163.                                                                       this.labelFilename});
  164.         this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
  165.         this.Name = "MainForm";
  166.         this.Text = "CaptureSound";
  167.         this.Closing += new System.ComponentModel.CancelEventHandler(this.MainForm_Closing);
  168.         this.ResumeLayout(false);
  169.  
  170.     }
  171.     #endregion
  172.     private void InitDirectSound()
  173.     {
  174.         CaptureBufferSize = 0;
  175.         NotifySize = 0;
  176.  
  177.         // Create DirectSound.Capture using the preferred capture device
  178.         try
  179.         {
  180.             applicationDevice = new Capture(CaptureDeviceGuid);
  181.         }
  182.         catch {}
  183.     }
  184.     void CreateCaptureBuffer()
  185.     {
  186.         //-----------------------------------------------------------------------------
  187.         // Name: CreateCaptureBuffer()
  188.         // Desc: Creates a capture buffer and sets the format 
  189.         //-----------------------------------------------------------------------------
  190.         CaptureBufferDescription dscheckboxd = new CaptureBufferDescription(); 
  191.  
  192.         if (null != applicationNotify)
  193.         {
  194.             applicationNotify.Dispose();
  195.             applicationNotify = null;
  196.         }
  197.         if (null != applicationBuffer)
  198.         {
  199.             applicationBuffer.Dispose();
  200.             applicationBuffer = null;
  201.         }
  202.  
  203.         if (0 == InputFormat.Channels)
  204.             return;
  205.  
  206.         // Set the notification size
  207.         NotifySize = (1024 > InputFormat.AverageBytesPerSecond / 8) ? 1024 : (InputFormat.AverageBytesPerSecond / 8);
  208.         NotifySize -= NotifySize % InputFormat.BlockAlign;   
  209.  
  210.         // Set the buffer sizes
  211.         CaptureBufferSize = NotifySize * NumberRecordNotifications;
  212.  
  213.         // Create the capture buffer
  214.         dscheckboxd.BufferBytes = CaptureBufferSize;
  215.         InputFormat.FormatTag = WaveFormatTag.Pcm;
  216.         dscheckboxd.Format = InputFormat; // Set the format during creatation
  217.         
  218.         applicationBuffer = new CaptureBuffer(dscheckboxd, applicationDevice);
  219.         NextCaptureOffset = 0;
  220.  
  221.         InitNotifications();
  222.     }
  223.     void InitNotifications()
  224.     {
  225.         //-----------------------------------------------------------------------------
  226.         // Name: InitNotifications()
  227.         // Desc: Inits the notifications on the capture buffer which are handled
  228.         //       in the notify thread.
  229.         //-----------------------------------------------------------------------------
  230.  
  231.         if (null == applicationBuffer)
  232.             throw new NullReferenceException();
  233.         
  234.         // Create a thread to monitor the notify events
  235.         if (null == NotifyThread)
  236.         {
  237.             NotifyThread = new Thread(new ThreadStart(WaitThread));
  238.             Capturing = true;
  239.             NotifyThread.Start();
  240.  
  241.             // Create a notification event, for when the sound stops playing
  242.             NotificationEvent = new AutoResetEvent(false);
  243.         }
  244.  
  245.  
  246.         // Setup the notification positions
  247.         for (int i = 0; i < NumberRecordNotifications; i++)
  248.         {
  249.             PositionNotify[i].Offset = (NotifySize * i) + NotifySize - 1;
  250.             PositionNotify[i].EventNotifyHandle = NotificationEvent.Handle;
  251.         }
  252.         
  253.         applicationNotify = new Notify(applicationBuffer);
  254.  
  255.         // Tell DirectSound when to notify the app. The notification will come in the from 
  256.         // of signaled events that are handled in the notify thread.
  257.         applicationNotify.SetNotificationPositions(PositionNotify, NumberRecordNotifications);
  258.     }
  259.     private void checkboxRecord_CheckedChanged(object sender, System.EventArgs e)
  260.     {
  261.         Recording = !Recording;
  262.         StartOrStopRecord(Recording);
  263.  
  264.         if (!Recording)
  265.             checkboxRecord.Enabled = false;
  266.     }
  267.     void StartOrStopRecord(bool StartRecording)
  268.     {
  269.         //-----------------------------------------------------------------------------
  270.         // Name: StartOrStopRecord()
  271.         // Desc: Starts or stops the capture buffer from recording
  272.         //-----------------------------------------------------------------------------
  273.  
  274.         if (StartRecording)
  275.         {
  276.             // Create a capture buffer, and tell the capture 
  277.             // buffer to start recording   
  278.             CreateCaptureBuffer();
  279.             applicationBuffer.Start(true);
  280.         }
  281.         else
  282.         {
  283.             // Stop the buffer, and read any data that was not 
  284.             // caught by a notification
  285.             applicationBuffer.Stop();
  286.  
  287.             RecordCapturedData();
  288.             
  289.             Writer.Seek(4, SeekOrigin.Begin); // Seek to the length descriptor of the RIFF file.
  290.             Writer.Write((int)(SampleCount + 36));    // Write the file length, minus first 8 bytes of RIFF description.
  291.             Writer.Seek(40, SeekOrigin.Begin); // Seek to the data length descriptor of the RIFF file.
  292.             Writer.Write(SampleCount); // Write the length of the sample data in bytes.
  293.             
  294.             Writer.Close();    // Close the file now.
  295.             Writer = null;    // Set the writer to null.
  296.             WaveFile = null; // Set the FileStream to null.
  297.         }
  298.     }
  299.     void CreateRIFF()
  300.     {
  301.         /**************************************************************************
  302.              
  303.             Here is where the file will be created. A
  304.             wave file is a RIFF file, which has chunks
  305.             of data that describe what the file contains.
  306.             A wave RIFF file is put together like this:
  307.              
  308.             The 12 byte RIFF chunk is constructed like this:
  309.             Bytes 0 - 3 :    'R' 'I' 'F' 'F'
  310.             Bytes 4 - 7 :    Length of file, minus the first 8 bytes of the RIFF description.
  311.                             (4 bytes for "WAVE" + 24 bytes for format chunk length +
  312.                             8 bytes for data chunk description + actual sample data size.)
  313.             Bytes 8 - 11:    'W' 'A' 'V' 'E'
  314.             
  315.             The 24 byte FORMAT chunk is constructed like this:
  316.             Bytes 0 - 3 :    'f' 'm' 't' ' '
  317.             Bytes 4 - 7 :    The format chunk length. This is always 16.
  318.             Bytes 8 - 9 :    File padding. Always 1.
  319.             Bytes 10- 11:    Number of channels. Either 1 for mono,  or 2 for stereo.
  320.             Bytes 12- 15:    Sample rate.
  321.             Bytes 16- 19:    Number of bytes per second.
  322.             Bytes 20- 21:    Bytes per sample. 1 for 8 bit mono, 2 for 8 bit stereo or
  323.                             16 bit mono, 4 for 16 bit stereo.
  324.             Bytes 22- 23:    Number of bits per sample.
  325.             
  326.             The DATA chunk is constructed like this:
  327.             Bytes 0 - 3 :    'd' 'a' 't' 'a'
  328.             Bytes 4 - 7 :    Length of data, in bytes.
  329.             Bytes 8 -...:    Actual sample data.
  330.             
  331.         ***************************************************************************/
  332.  
  333.         // Open up the wave file for writing.
  334.         WaveFile = new FileStream(FileName, FileMode.Create);
  335.         Writer = new BinaryWriter(WaveFile);
  336.  
  337.         // Set up file with RIFF chunk info.
  338.         char[] ChunkRiff = {'R','I','F','F'};
  339.         char[] ChunkType = {'W','A','V','E'};
  340.         char[] ChunkFmt    = {'f','m','t',' '};
  341.         char[] ChunkData = {'d','a','t','a'};
  342.             
  343.         short shPad = 1; // File padding
  344.         int nFormatChunkLength = 0x10; // Format chunk length.
  345.         int nLength = 0; // File length, minus first 8 bytes of RIFF description. This will be filled in later.
  346.         short shBytesPerSample = 0; // Bytes per sample.
  347.  
  348.         // Figure out how many bytes there will be per sample.
  349.         if (8 == InputFormat.BitsPerSample && 1 == InputFormat.Channels)
  350.             shBytesPerSample = 1;
  351.         else if ((8 == InputFormat.BitsPerSample && 2 == InputFormat.Channels) || (16 == InputFormat.BitsPerSample && 1 == InputFormat.Channels))
  352.             shBytesPerSample = 2;
  353.         else if (16 == InputFormat.BitsPerSample && 2 == InputFormat.Channels)
  354.             shBytesPerSample = 4;
  355.  
  356.         // Fill in the riff info for the wave file.
  357.         Writer.Write(ChunkRiff);
  358.         Writer.Write(nLength);
  359.         Writer.Write(ChunkType);
  360.  
  361.         // Fill in the format info for the wave file.
  362.         Writer.Write(ChunkFmt);
  363.         Writer.Write(nFormatChunkLength);
  364.         Writer.Write(shPad);
  365.         Writer.Write(InputFormat.Channels);
  366.         Writer.Write(InputFormat.SamplesPerSecond);
  367.         Writer.Write(InputFormat.AverageBytesPerSecond);
  368.         Writer.Write(shBytesPerSample);
  369.         Writer.Write(InputFormat.BitsPerSample);
  370.             
  371.         // Now fill in the data chunk.
  372.         Writer.Write(ChunkData);
  373.         Writer.Write((int)0);    // The sample length will be written in later.
  374.     }
  375.  
  376.     void RecordCapturedData() 
  377.     {
  378.         //-----------------------------------------------------------------------------
  379.         // Name: RecordCapturedData()
  380.         // Desc: Copies data from the capture buffer to the output buffer 
  381.         //-----------------------------------------------------------------------------
  382.         byte[] CaptureData = null;
  383.         int ReadPos;
  384.         int CapturePos;
  385.         int LockSize;
  386.  
  387.         applicationBuffer.GetCurrentPosition(out CapturePos, out ReadPos);
  388.         LockSize = ReadPos - NextCaptureOffset;
  389.         if (LockSize < 0)
  390.             LockSize += CaptureBufferSize;
  391.  
  392.         // Block align lock size so that we are always write on a boundary
  393.         LockSize -= (LockSize % NotifySize);
  394.  
  395.         if (0 == LockSize)
  396.             return;
  397.  
  398.         // Read the capture buffer.
  399.         CaptureData = (byte[])applicationBuffer.Read(NextCaptureOffset, typeof(byte), LockFlag.None, LockSize);
  400.  
  401.         // Write the data into the wav file
  402.         Writer.Write(CaptureData, 0, CaptureData.Length);
  403.         
  404.         // Update the number of samples, in bytes, of the file so far.
  405.         SampleCount += CaptureData.Length;
  406.  
  407.         // Move the capture offset along
  408.         NextCaptureOffset += CaptureData.Length; 
  409.         NextCaptureOffset %= CaptureBufferSize; // Circular buffer
  410.     }
  411.  
  412.     private void buttonSoundfile_Click(object sender, System.EventArgs e)
  413.     {
  414.         OnCreateSoundFile();
  415.     }
  416.  
  417.     private void OnCreateSoundFile()
  418.     {
  419.         //-----------------------------------------------------------------------------
  420.         // Name: OnCreateSoundFile()
  421.         // Desc: Called when the user requests to save to a sound file
  422.         //-----------------------------------------------------------------------------
  423.  
  424.         SaveFileDialog ofd = new SaveFileDialog();
  425.         WaveFormat wf = new WaveFormat();
  426.         DialogResult result;
  427.  
  428.         // Get the default media path (something like C:\WINDOWS\MEDIA)
  429.         if (string.Empty == Path)
  430.             Path = Environment.SystemDirectory.Substring(0, Environment.SystemDirectory.LastIndexOf("\\")) + "\\media";
  431.  
  432.         if (Recording)
  433.         {
  434.             // Stop the capture and read any data that 
  435.             // was not caught by a notification
  436.             StartOrStopRecord(false);
  437.             Recording = false;
  438.         }
  439.  
  440.         ofd.DefaultExt = ".wav";
  441.         ofd.Filter = "Wave Files|*.wav|All Files|*.*";
  442.         ofd.FileName = FileName;
  443.         ofd.InitialDirectory = Path;
  444.         
  445.         // Update the UI controls to show the sound as loading a file
  446.         checkboxRecord.Enabled = false;
  447.         labelFilename.Text = "Saving file...";
  448.  
  449.         // Display the OpenFileName dialog. Then, try to load the specified file
  450.         result = ofd.ShowDialog(this);
  451.  
  452.         if (DialogResult.Abort == result || DialogResult.Cancel == result)
  453.         {
  454.             labelFilename.Text = "Save aborted.";
  455.             return;
  456.         }
  457.         FileName = ofd.FileName;        
  458.  
  459.         CreateRIFF();
  460.  
  461.         // Update the UI controls to show the sound as the file is loaded
  462.         labelFilename.Text = FileName;
  463.         checkboxRecord.Enabled = true;
  464.  
  465.         // Remember the path for next time
  466.         Path = FileName.Substring(0, FileName.LastIndexOf("\\"));
  467.     }
  468.  
  469.     private void WaitThread()
  470.     {
  471.         while(Capturing)
  472.         {
  473.             //Sit here and wait for a message to arrive
  474.             NotificationEvent.WaitOne(Timeout.Infinite, true);
  475.             RecordCapturedData();
  476.         }
  477.     }
  478.  
  479. }
  480.