confused why timer wont successfully execute sub

busterbot

New member
Joined
Feb 29, 2012
Messages
4
Programming Experience
1-3
So I can't figure out why this fails when run from a timer, but executes fine through button control. Using alvas.audio to capture wave file, when detecting a "silent" period it saves the file, processes it and then the idea is to delete that file and start a new recording.

The problem is when I do through through a push button, it works fine, the file is captured, processed and clicking the button calls StartRecord() to start a new recording which enables the timer to look for the silent period but does not rely on the timer to restart the recording after processing - no issues I can do this over and over without a problem.

Having it automated through a timer control it fails, after the first recording the timer should restart the recording by calling StartRecord() again but I get an error "The specified device handle is invalid". I thought maybe it was all happening too fast so I tried sleeping in between the restart for up to a full minute with no change, I removed the ProcessSpeech() sub routine from the timer and just had it record, and sleep for a minute then restart and still fail with same error. I can do the manual button start much faster than a minute with no issue.

VB.NET:
[LEFT]

    Private Sub StartRecord()


        Dim fileName As String = "c:\test.wav"
        Dim stream As Stream = Nothing
        Dim arw As IAudioReadWriter = Nothing


        tmrCapture.Enabled = False


        If File.Exists(fileName) Then
            File.Delete(fileName)
            stream = File.Create(fileName)
        Else
            stream = File.Create(fileName)
        End If


        arw = New WaveReadWriter(stream, AudioCompressionManager.FormatBytes(m_format))
        rp.Open(arw)


        rp.Record()  'start recording - this is where it fails when being called from the timer after the first recording 
        
    End Sub


    Private Sub tmrCapture_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrCapture.Tick
        If SilentCounter >= LastDuration Then LastDuration = SilentCounter


        If SilentCounter <= LastDuration Then
            TextBox1.Text = "Finished"
            SilentCounter = 0
            LastDuration = 0
            rp.Stop()   'stop recording
            rp.Close()  'close file to process

            ProcessSpeech() 'process file for speech reco


            StartRecord()  'Start listening again for next capture


            tmrCapture.Enabled = False
        End If
    End Sub



[/LEFT]
 
Last edited:
Sorry fixed, for some reason when I use Chrome it comes out wacky but worked in IE. I'm new to vb.net, long time user of vb6 but just hobby programmer so I've never been really good.
 
I'm guessing that whatever 'rp' refers to has been disposed or otherwise destroyed, because the error message suggests that its handle has been released back to the system. Where and how else are you using that object/variable?
 
Entire code below. RP is declared right under the form class, but if RP was disposed then wouldn't I get an error on the line before, rp.open? That rp.open(arw) executes fine and creates the new file to record to. It's only when it hits the rp.record() call where it fails. Alvas.net doesn't seem to answer any support questions anymore and they have no forum on their site.

What's so confusing to me is that it only fails when calling startrecord() sub from the timer, if I comment out startrecord under tmrCapture and just manually hit the record button on the form each time, which just calls startrecord() sub anyway, it works fine. It's only when called from the timer where it fails. Really doesn't make any sense to me at all.


VB.NET:
Imports System.ComponentModel
Imports System.IO
Imports Alvas.Audio


Public Class MainForm
    Public SilentCounter As Integer = 0 'Count how long silent periods are for timer to know when to capture wav
    Public rp As New RecordPlayer()
    Dim LastDuration As Integer


    Public Sub New()
        InitializeComponent()
        Init()
    End Sub


    Private Sub Init()
        tspProgress.Maximum = Short.MaxValue
        AddHandler rp.PropertyChanged, AddressOf rp_PropertyChanged  'to update status on mic levels and record durations
        rp.SkipSilent = True 'essentially pauses recording when mic levels are below "silentlevel"
        rp.SilentLevel = 1500


        'Bind to controls on form to override silent thresholds set above
        cbSkipSilent.DataBindings.Add("Checked", rp, RecordPlayer.SkipSilentProperty, False, DataSourceUpdateMode.OnPropertyChanged)
        nudSilentLevel.DataBindings.Add("Value", rp, RecordPlayer.SilentLevelProperty, False, DataSourceUpdateMode.OnPropertyChanged)
    End Sub


    Private Sub tsbStop_Click(ByVal sender As Object, ByVal e As EventArgs) Handles tsbStop.Click
        rp.Stop()
        rp.Close()
    End Sub


    Private Sub tsbPause_Click(ByVal sender As Object, ByVal e As EventArgs) Handles tsbPause.Click
        rp.Pause()
    End Sub


    Private Sub tsbPlay_Click(ByVal sender As Object, ByVal e As EventArgs) Handles tsbPlay.Click
        rp.Play(0)
    End Sub


    Private Sub tsbRecord_Click(ByVal sender As Object, ByVal e As EventArgs) Handles tsbRecord.Click
        StartRecord()
    End Sub




    Private Sub rp_PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
        Select Case e.PropertyName
            Case RecordPlayer.VolumeLevelProperty
                tspProgress.Value = rp.VolumeLevel  'show mic levels while recording
                Exit Select
            Case RecordPlayer.PositionProperty, RecordPlayer.DurationProperty
                tsslPosition.Text = String.Format("{0} : {1}", rp.Position, rp.Duration) 'Show length of current recording in ms
                If rp.Position <> 0 Then
                    SilentCounter = rp.Duration
                    tmrCapture.Enabled = True   'enable timer to check for silent period
                End If


                Exit Select
            Case Else


                Exit Select
        End Select
    End Sub


    Private Sub StartRecord()
        Dim fileName As String = "c:\test.wav"
        Dim stream As Stream = Nothing
        Dim arw As IAudioReadWriter = Nothing


        tmrCapture.Enabled = False  'disable silence checker until next recording begins


        'Clean up existing file for new record if any
        If File.Exists(fileName) Then
            File.Delete(fileName)
            stream = File.Create(fileName)
        Else
            stream = File.Create(fileName)
        End If


        Dim wavformat As IntPtr = AudioCompressionManager.GetPcmFormat(2, 16, 16000)


        arw = New WaveReadWriter(stream, AudioCompressionManager.FormatBytes(wavformat))
        rp.Open(arw)


        rp.Record()  'start recording - this is where it fails when being called from the timer after the first recording 


    End Sub


    Private Sub tmrCapture_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrCapture.Tick
        If SilentCounter >= LastDuration Then LastDuration = SilentCounter


        If SilentCounter <= LastDuration Then
            TextBox1.Text = "Finished"
            SilentCounter = 0
            LastDuration = 0
            rp.Stop()  'Stop Recording
            rp.Close() 'Close file to processing


            ProcessSpeech() 'process file for speech reco


            StartRecord()  'Start listening again for next capture
            tmrCapture.Enabled = False
        End If
    End Sub


    Private Function GetProcessText(ByVal process As String, ByVal args As String, ByVal workingdir As String) As String
        Dim p As Process = New Process
        ' this is the name of the process we want to execute 
        p.StartInfo.FileName = process
        If Not (workingdir = "") Then
            p.StartInfo.WorkingDirectory = workingdir
        End If
        p.StartInfo.Arguments = args
        p.StartInfo.CreateNoWindow = True
        ' need to set this to false to redirect output
        p.StartInfo.UseShellExecute = False
        p.StartInfo.RedirectStandardOutput = True
        ' start the process 
        p.Start()
        ' read all the output
        ' here we could just read line by line and display it
        ' in an output window 
        Dim output As String = p.StandardOutput.ReadToEnd
        ' wait for the process to terminate 
        p.WaitForExit()
        Return output
    End Function


    Private Sub ProcessSpeech()
        TextBox1.Text += GetProcessText("c:\sox.exe", "c:\test.wav c:\test.flac rate 8k", "c:\")


        Dim Wget As String = "-q -U ""Mozilla/5.0"" --post-file C:\test.flac --header=""Content-Type: audio/x-flac; rate=8000"" -O - ""http://www.google.com/speech-api/v1/recognize?lang=en-US&client=chromium"""
        Dim captured As String = GetProcessText("c:\wget.exe", Wget, "c:\")
        TextBox1.Text = captured
        TextBox1.Refresh()
    End Sub


End Class
 
In button Click you call StartRecord, but in timer Tick you first call rp.Stop and rp.Close (and set some variables). It is not uncommon that Close methods have same functionality as Dispose method and can be used interchangeably.
 
Right, but if I remove the StartRecord from the timer, so that it does still stop and close etc. But relies on the user to click the button to startrecord it works fine. rp.stop/close is still being called before the button click from the timer before a new recording is initiated.

I really have racked my brain and scoured the internet for an answer, it's baffling.
 
Back
Top