Audio Recorder

Audio Recorder is a simple Sound Recorder using the AudioCaptureDevice and CaptureSource support in Silverlight 4, recordings can be saved as a PCM Wave File using an AudioSink object.

www.cespage.com/silverlight/sl4tut20.html

Step 1

Start Microsoft Visual Web Developer 2010 Express, then Select File then New Project... Select "Visual Basic" then "Silverlight Application" from Templates, select a Location if you wish, then enter a name for the Project and then click OK, see below:

New Project

Step 2

New Silverlight Application window should appear, uncheck the box "Host the Silverlight Application in a new Web site" and then select the required Silverlight Version, see below:

New Silverlight Application

Step 3

A Blank Page named MainPage.xaml should then appear, see below:

MainPage.xaml

Step 4

Select Project then "Add Class...", and select the "Class" Template if not already Selected, then change the "Name" to MemoryAudioSink.vb , see below:

MemoryAudioSink Class

Step 5

Add the new Class to the Project by Clicking on Add, then in the Code View for the MemoryAudioSink.vb Class, above the "Public Class MemoryAudioSink" line type the following:

Imports System.IO

See Below:

MemoryAudioSink Class References

Step 6

While still in the Code View for the MemoryAudioSink.vb Class, below the "Public Class MemoryAudioSink" line type the following:

  Inherits AudioSink
Private _stream As MemoryStream ' Memory Sink
Private _format As AudioFormat ' Recording Format

See Below:

MemoryAudioSink Class Declarations

Step 7

While still in the Code View for the MemoryAudioSink Class, above the "End Class" for "Public Class MemoryAudioSink", type the following Subs:

Protected Overloads Overrides Sub OnCaptureStarted()
  _stream = New MemoryStream(1024)
End Sub

Protected Overloads Overrides Sub OnCaptureStopped()
End Sub

Protected Overloads Overrides Sub OnFormatChange(ByVal Format As AudioFormat)
  If Format.WaveFormat <> WaveFormatType.PCM Then
    Throw New InvalidOperationException("MemoryAudioSink only supports PCM Audio")
  End If
  _format = Format
End Sub

Protected Overloads Overrides Sub OnSamples(ByVal sampleTime As Long, _
    ByVal sampleDuration As Long, _
    ByVal sampleData As Byte())
  _stream.Write(sampleData, 0, sampleData.Length) ' Write Audio to Stream
End Sub

See Below:

MemoryAudioSink Class Subs

Step 8

While still in the Code View for the MemoryAudioSink Class, above the "End Class" for "Public Class MemoryAudioSink", type the following Properties:

Public ReadOnly Property BackingStream() As Stream
  Get
    Return _stream
  End Get
End Property

Public ReadOnly Property CurrentFormat() As AudioFormat
  Get
    Return _format
  End Get
End Property

See Below:

MemoryAudioSink Class Properties

Step 9

Return to the MainPage Designer View by selecting the "MainPage.xaml" Tab.
Then from the All Silverlight Controls section in the Toolbox select the Canvas control:

Canvas Control

Step 10

Draw a Two Canvases on the Page then in the XAML Pane above the "</Grid>" then change the "<Canvas>" lines to the following:

<Canvas Height="35" Width="400" VerticalAlignment="Top" HorizontalAlignment="Left" Name="Toolbar"></Canvas>
<Canvas Height="35" Width="400" Margin="0,265,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Name="StatusBar"></Canvas>

See below:

MainPage with Toolbar Canvas and Statusbar Canvas

Step 11

Then from the Common Silverlight Controls section in the Toolbox select the Button control:

Button Control

Step 12

Draw Three Buttons on the "Toolbar" Canvas by dragging the Buttons from the Toolbox onto the Canvas then in the XAML Pane inbetween the "<Canvas>" and "</Canvas>" tags change the "<Button>" lines to the following:

<Button Canvas.Left="6" Canvas.Top="6" Height="23" Width="75" Name="Record" Content="Record"/>
<Button Canvas.Left="87" Canvas.Top="6" Height="23" Width="75" Name="Stop" Content="Stop"/>
<Button Canvas.Left="168" Canvas.Top="6" Height="23" Width="75" Name="Save" Content="Save..."/> 

See below:

MainPage with Canvas and Buttons

Step 13

Then from the Common Silverlight Controls section in the Toolbox select the TextBlock control:

TextBlock Control

Step 14

Draw a TextBlock onto the Lower Canvas (StatusBar) and change the <TextBlock> line to the following:

<TextBlock Canvas.Left="6" Canvas.Top="6" Height="23" Width="318" FontSize="16" Name="Status"/>

See below:

MainPage with Buttons and TextBlock

Step 15

Right Click on the Page or the entry for "MainPage.xaml" in Solution Explorer and choose the "View Code" option. In the Code View above the "Partial Public Class MainPage" line type the following:

Imports System.IO

See Below:

MainPage.xaml Imports

Step 16

While still in the Code View for MainPage.xaml, below the "Inherits UserControl" line type the following:

Private _source As New CaptureSource
Private _sink As MemoryAudioSink
Private _isRecording As Boolean
Private _hasRecorded As Boolean

See Below:

MainPage.xaml Declarations

Step 17

While still in the Code View, in the "Public Sub New()" constructor below the "InitializeComponent()" line type the following:

_source.AudioCaptureDevice = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice

See Below:

MainPage.xaml Constructor

Step 18

While still in the Code View, below the "End Sub" for "Public Sub New()" Constructor type the following Function and Sub:

Private Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
  target = value
  Return value
End Function

Public Sub SavePcmToWav(ByRef Data As Stream, ByRef Output As Stream, _
    ByRef Format As AudioFormat)
  If Format.WaveFormat <> WaveFormatType.PCM Then
    Throw New ArgumentException("Only PCM coding is supported.")
  End If
  Dim _output As New BinaryWriter(Output)
  ' WAV header
  _output.Write("RIFF".ToCharArray()) ' RIFF chunk
  _output.Write(CUInt((Data.Length + 36))) ' Total Length Of Package To Follow
  _output.Write("WAVE".ToCharArray()) ' WAV chunk
  _output.Write("fmt ".ToCharArray()) ' FORMAT chunk
  _output.Write(CUInt(&H10)) ' Length Of FORMAT Chunk (Binary, always 0x10)
  _output.Write(CUShort(&H1)) ' Always 0x01
  ' Channel Numbers (Always 0x01=Mono, 0x02=Stereo)
  _output.Write(CUShort(Format.Channels))
  _output.Write(CUInt(Format.SamplesPerSecond)) ' Sample Rate (Binary, in Hz)
  _output.Write(CUInt((Format.BitsPerSample * _
  Format.SamplesPerSecond * _
    Format.Channels / 8))) ' Bytes Per Second
  ' Bytes Per Sample: 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo
  _output.Write(CUShort((Format.BitsPerSample * _
    Format.Channels / 8)))
  _output.Write(CUShort(Format.BitsPerSample)) ' Bits Per Sample
  _output.Write("data".ToCharArray()) ' DATA chunk
  _output.Write(CUInt(Data.Length)) ' Length Of Data To Follow
  ' Raw PCM data
  Dim originalPosition As Long = Data.Position ' Remember Original Position
  Data.Seek(0, SeekOrigin.Begin) ' Reset position in Data
  ' Append all data from Data stream into output stream.
  Dim buffer As Byte() = New Byte(4095) {}
  Dim read As Integer ' number of bytes read in one iteration
  While (InlineAssignHelper(read, Data.Read(buffer, 0, 4096))) > 0
    _output.Write(buffer, 0, read)
  End While
  Data.Seek(originalPosition, SeekOrigin.Begin) ' Restore Original Position
End Sub

See Below:

MainPage.xaml SavePcMtoWav Sub and Helper Function

Step 19

Return to the Designer View by selecting the "MainPage.xaml" Tab, or Right Click on the Page or the Entry for "MainPage.xaml" in Solution Explorer and choose the "View Designer" option.
Double Click on the "Record" Button and type in the Record_Click Sub:

If Not _isRecording Then
  If (CaptureDeviceConfiguration.AllowedDeviceAccess _
      Or CaptureDeviceConfiguration.RequestDeviceAccess) _
      And _source.State = CaptureState.Stopped Then
    _sink = New MemoryAudioSink
    _sink.CaptureSource = _source
    _source.Start()
    _isRecording = True
    _hasRecorded = False
    Status.Text = "Recording..."
  End If
End If

See Below:

Record Button Click Event

Step 20

Return to the Designer View by selecting the "MainPage.xaml" Tab, or Right Click on the Page or the Entry for "MainPage.xaml" in Solution Explorer and choose the "View Designer" option.
Double Click on the "Stop" Button and type in the Stop_Click Sub:

If _source.State = CaptureState.Started Then
  _source.Stop()
  _isRecording = False
  _hasRecorded = True
  Status.Text = "Recording Complete"
End If

See Below:

Stop Button Click Event

Step 21

Return to the Designer View by selecting the "MainPage.xaml" Tab, or Right Click on the Page or the Entry for "MainPage.xaml" in Solution Explorer and choose the "View Designer" option.
Double Click on the "Save..." Button Control and type in the Save_Click Sub:

If _hasRecorded Then
  Dim SaveDialog As New SaveFileDialog
  SaveDialog.Filter = "Audio Files (*.wav)|*.wav"
  If SaveDialog.ShowDialog Then
    Try
      Status.Text = "Saving..."
      Using FileStream As Stream = SaveDialog.OpenFile
        SavePcmToWav(_sink.BackingStream, FileStream, _sink.CurrentFormat)
      End Using
      Status.Text = "Recording Saved"
    Catch ex As Exception
      ' Ignore Errors
    End Try
  End If
Else
  MessageBox.Show("Complete a Recording before Saving", _
    "Audio Recorder", MessageBoxButton.OK)
End If

See Below:

Save Button Click Event

Step 22

Save the Project as you have now finished the Silverlight application. Select Debug then Start Debugging or click on Start Debugging:

Start Debugging

After you do, the following will appear in a new Web Browser window:

Application Running

Step 23

Click on "Record", a dialog will appear saying "Do you want to allow camera and microphone access?", select "Yes" and Recording should begin, when finished click Stop then Save your Recording, see below:

Audio Recorder

Step 24

Close the Application and Browser window by clicking on the Close Button Close on the top right of the Application Window and Web Browser to Stop the application.

This was a simple Audio or Microphone-based application, it could also show more visual indications of a Recording in progress between the Toolbar and Statusbar, it is up to you!
If you could not get your Audio feed to work try Right Clicking on the Silverlight application, click the "Silverlight" option, then select the Webcam / Mic Tab to choose your Microphone as the Default Audio Source.