Add Voice Recording with eMbedded Visual Basic

 

Get started adding voice recording to your eMbedded Visual Basic applications using the native Windows CE VoiceRecorder control and some Windows CE API calls.

Applies to:
   Microsoft Windows Powered Pocket PC 2000
   Microsoft eMbedded Visual Basic
   Microsoft eMbedded Visual Tools
   Download 761-CF-DEV.exe

Gotchas

If you create a "modeless" VoiceRecorder control, the control gets destroyed when it loses the focus (e.g., when you tap on the form of the application). You should therefore use the control as "modal" except when you want to interact with the control from your application without any user interaction (just as I have done with instant recording below). If you want to prevent a "modeless" control from being destroyed, you could capture the WM_ACTIVATE message using the technique I have described in my article Capture Windows Messages Using the MessageCE Control.

Languages Supported

English

Voice-Recording Scenarios

You have probably already found the voice-recording (or other sound) functionality to be useful when implemented in the Pocket PC's Notes application. There are many situations where an instant voice message can be much more efficient than tapping out the message using the stylus. The classic example is the health care application where a doctor dictates examination records after seeing a patient. In addition many mobile workers could use this to take instant "notes" while on assignment.

What if you could provide this functionality from within your eMbedded Visual Basic applications? The main benefit would be that you remain in control while recording takes place. You could add the recording to a local database, send it to someone else using e-mail or even to a remote server. You would be able to add your own identification and even keywords to the recording.

The VoiceRecorder Control

There is a VoiceRecorder control available on a Pocket PC, but there is no Microsoft ActiveX control prepared for the eMbedded Visual Basic programmer. To access the VoiceRecorder control, we use Windows application programming interface (API) calls with some special handling to simulate user defined types (UDTs) that are also not available in eMbedded Visual Basic. I will come to that when I walk you through the sample code of this article, but first let's have a look at what you can do using this technique.

VoiceRecorder Sample

To show you how it works, I have put together a sample using eMbedded Visual Basic (see figure).

VoiceRecorder sample.

In the figure I have tapped the large Record button to bring up the VoiceRecorder control. And as soon as I tap the Record button on the control (red circle) the recording begins and the result is stored in a file named sound .wav (located in the \My Documents folder). I can use the control's Play button to listen to the recorded file and remove the control by either tapping OK or "X" (cancel; the recording will not be saved) on the control. When I tap the large Play button, I can listen to the recorded file. If I check the VoiceRecorder control moveable check box before I tap the Record button, I can move the control with the handle appearing on the left of the control.

Code Walk Through

In the form, a private variable holding the windows handle to the VoiceRecorder control is declared like this:

Private hVoiceRecorder As Long

When I tap the large Record button, the following line of code is executed:

hVoiceRecorder = ShowVoiceRecorder(hWnd, 8, 32, _
                 "\My Documents\sound.wav", True, _
                 (chkMoveable.Value = 1), True, False)

And the parameters of the ShowVoiceRecorder call are as follows:

  1. Window handle of the parent window (in this case the forms handle).
  2. Left position of the control to be created. Note that this (and the next) value are in screen pixels and not dependent on any ScaleMode you have defined for the form.
  3. Top position of the control to be created.
  4. Path and name of the file to save the recording to.
  5. Indication if the control should be "modal" or not. If the control is created modal, the user will have to close it (with the OK or "X" button on the control) before interacting with your form again.
  6. Indication whether the control should be moveable or not.
  7. Indication whether the record button (red circle) should be shown on the control.
  8. Indication whethr the file should be instantly played (only used for playing recorded files).

The return value is the windows handle of the newly created control.

And if you want to start the recording instantly after the control is created, you could use the following code.

hVoiceRecorder = ShowVoiceRecorder(hWnd, 8, 32, _
                 "\My Documents\sound.wav", False, _
                 (chkMoveable.Value = 1), True, False)
Call SendMessageString(hVoiceRecorder, VRM_RECORD, 0, 0)

The control is created with the Modal parameter set to False, and therefore you can send a message to start the recording to the control using the SendMessage Windows CE API.

Let's have a look at the ShowVoiceRecorder function:

Public Function ShowVoiceRecorder(ByVal hWnd As Long, ByVal xPos As Long, ByVal yPos As Long, ByVal FileName As String, ByVal Modal As Boolean, ByVal Moveable As Boolean, ByVal ShowRecButton As Boolean, ByVal PlayNow As Boolean) As Long
 
  Dim llpszFileName As Long
  Dim llLength As Long
  Dim llStyle As Long
  Dim udtCM_VOICE_RECORDER As String
  
  ' Allocate memory
  llLength = LenB(FileName) + 1
  llpszFileName = LocalAlloc(LMEM_ZEROINIT, llLength)
    
  ' Copy text from string to allocated memory
  MoveMemory llpszFileName, FileName, llLength
  
  ' Set style from parameters
  llStyle = VRS_NO_NOTIFY
  If Modal Then llStyle = llStyle + VRS_MODAL
  If Not Moveable Then llStyle = llStyle + VRS_NO_MOVE
  If Not ShowRecButton Then llStyle = llStyle + VRS_NO_RECORD
  If PlayNow Then llStyle = llStyle + VRS_PLAY_MODE
  
  ' Reserve 28 bytes for structure
  udtCM_VOICE_RECORDER = Space(14)
    
  ' Fill structure
  udtCM_VOICE_RECORDER = setCM_VOICE_RECORDER(llStyle, _
                         xPos, yPos, hWnd, llpszFileName)
  
  ' Create Voice Recorder
  ShowVoiceRecorder = VoiceRecorder_Create(udtCM_VOICE_RECORDER)
  
  ' Release allocated memory
  Call LocalFree(llpszFileName)
 
End Function

Since I need to have a long pointer to the file name string, I have to use some API calls to allocate memory and copy the passed string to that memory. Then, I set the style for the VoiceRecorder control to be created using the passed parameters. And before I make the call to the VoiceRecorder_Create API, I will fill the structure (CM_VOICE_RECORDER) that is passed as the only parameter to that call. Because eMbedded Visual Basic lacks the ability to create UDTs, I have to simulate the UDT using a string. That is done in the setCM_VOICE_RECORDER function:

Private Function setCM_VOICE_RECORDER(ByVal dwStyle As Long, ByVal xPos As Long, ByVal yPos As Long, hWndParent As Long, ByVal lpszRecordFilename As Long) As String
    
  ' Build CM_VOICE_RECORDER UDT:
  'Type CM_VOICE_RECORDER
  '  cb As Long
  '  dwStyle As Long
  '  xPos As Long
  '  yPos As Long
  '  hwndParent As Long
  '  id As Long
  '  lpszRecordFileName As Long
  'End Type
  
  setCM_VOICE_RECORDER = LongToBytes(28) & LongToBytes(dwStyle) & _
                         LongToBytes(xPos) & LongToBytes(yPos) & _
                         LongToBytes(hWndParent) & LongToBytes(0) & _
                         LongToBytes(lpszRecordFilename)
 
End Function

The comments in the code above show how the CM_VOICE_RECORDER type should have been defined if the support was implemented (just as you would have done in Visual Basic on your PC). Natively, the structure is defined like this (see the eMbedded Visual Tools Help file for more information).

typedef struct tagCM_VOICE_RECORDER {
  WORD cb;
  DWORD dwStyle;
  int xPos;
  int yPos;
  HWND hwndParent;
  int id;
  LPSTR lpszRecordFileName;
} CM_VOICE_RECORDER, *LPCM_VOICE_RECORDER;

And if you want to read more about using strings to create UDTs in eMbedded Visual Basic, please see the excellent article UDTs (User Defined Types) with VBCE! by Antonio Paneiro.

I also need a helper function (LongToBytes) to convert a Long value to a string:

Function LongToBytes(ByVal Value As Long) As String
  
  Dim lsHex As String, i As Integer
  
  lsHex = Right("00000000" & Hex(Value), 8)
  For i = 1 To 7 Step 2
    LongToBytes = ChrB(CInt("&H" & Mid(lsHex, i, 2))) & LongToBytes
  Next
 
End Function

When I tap the Play button in the sample, I launch the following code:

PlaySoundFile "\My Documents\sound.wav"

And the PlaySoundFile implementation looks like this:

Public Sub PlaySoundFile(ByVal FileName As String)
 
  Call PlaySound(FileName, 0, SND_FILENAME + SND_SYNC + SND_NODEFAULT)
 
End Sub

If you want to use the VoiceRecorder control to play the file, you could use the following code:

hVoiceRecorder = ShowVoiceRecorder(hWnd, 8, 32, _
                 "\My Documents\sound.wav", True, _
                 (chkMoveable.Value = 1), False, True)

For a complete example, download 761-CF-DEV.exe.

Using the Recording

If you want to use the VoiceRecorder control in your application, you would probably like to read the recorded sound file (.wav). You can do this using the File object in the free OSIUtil control from Odyssey Software. I have described the process in my article Using the OSI Utility Library from eMbedded Visual Basic. When you have read the file into a variable in your application, you could store it in a local database or send it off to a server.

Conclusion

You can now start adding voice-recording functionality to your applications. You have several options for storing the result of recordings and it's really up to your (and your users') imagination how you use this capability. Make a prototype and ask them how they like it, and sooner than you think you will be off implementing a voice-enabled application.