Android Question "Rewind" function of audiostreamer

Olivier Zeegers

Member
Licensed User
Hello Erel,

I have found the documentation of Audiostreamer in this link: https://www.b4x.com/android/help/audio.html#audiostreamer

I can understand that one can start a recording and later on play it back but I don't see any function to start playing back from a certain position in the stream ?
Like i asked, I want to build an app that mimics a dictaphone, a sort of voice recorder. But the user should be able to press the record button and record his voice and then if he made a mistake pus the rewind button to rewind a little and then either OVERWRITE his previous recording of INSERT a new voice recording.

I try to make a better version of the app dictate + connect: http://www.dictate-connect.com/?page_id=134

You said this was possible in your previous answer to my question, so I bought B4A and B4I, but I don't see how ?

thank you
 

Olivier Zeegers

Member
Licensed User
hi,

Giving it a few seconds give the same result..
I checked the permissions in the settings and it said microphone and storage permission OK.

I will try with different samplerates and look for a mate with a different device..

strange though... can we run another test ?


I installed the original ARTEST from your AudioRecord thread and in Release mode it works on my OnePlus 5T.... It records a few seconds and then plays the recorded audio...
So there's no problem with the settings for AR, should be something else....
 
Upvote 0

Olivier Zeegers

Member
Licensed User
Have you used the device for any type of recording from the mic in the past. Did it work with the Audiostreamer when you tried it?

You could download a dictaphone type app from the play store and see if it works with that.

Yes I constantly use the dictate and connect app and it works well...
All other voice recorder type apps I downloaded work OK.. Strange..

Your audio record example works OK in release mode on my phone now..
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Your audio record example works OK in release mode on my phone now

If it has started working, can you try the dictaphone project again. There is nothing different in the way the two record audio. You will still have to run it in Release mode.
 
Upvote 0

Olivier Zeegers

Member
Licensed User
Hello,

When I run the artest project in release mode from the pc the app crashes with an error saying on the phone:

An error has occurred in sub:main_rec_ended (java line 611)
java.lang.illegalStateException: stop() called on an uninitialized AudioRecord.
continue ?

When I then stop the app and clear it from memory on the phone and start it again by hitting the app icon on the phone it runs perfectly without problems ??

Strange he ?

It records and plays back the stream !

No idea what is going on.

by the way how is the sub rec_ended called ? How does the thread know there is a rec_ended sub ? I don't see any reference ?

thank you
 
Upvote 0

Olivier Zeegers

Member
Licensed User
and your DictaPhone project works the same way ! When I let the app install and then shut down the app completely on the phone and start again from the app icon it works like it should do !
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Well, that's a step forward, finding out why could be difficult.

Rec ended is called in the ARTest projects because the thread is initialized with Rec as the eventName. In the Dictaphone app the eventname is Recording. The thread will automatically call the eventname_Ended sub when it finishes for any reason.
 
Upvote 0

Olivier Zeegers

Member
Licensed User
Well, that's a step forward, finding out why could be difficult.

Rec ended is called in the ARTest projects because the thread is initialized with Rec as the eventName. In the Dictaphone app the eventname is Recording. The thread will automatically call the eventname_Ended sub when it finishes for any reason.

In ARTest the thread that is started is called 'Recording' ? with a Sub Recording that is run... How does it come to rec_ended and not Recording_ended ?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
The sub is called Recording, but the Thread object is initialized as "Rec"

B4X:
'Initialize the thread to record on
    Record.Initialise("Rec")

Which then calls the sub Recording:

B4X:
    'Start the thread to record on
    Record.Start(Me,"Recording",Null)
 
Upvote 0

Olivier Zeegers

Member
Licensed User
The sub is called Recording, but the Thread object is initialized as "Rec"

B4X:
'Initialize the thread to record on
    Record.Initialise("Rec")

Which then calls the sub Recording:

B4X:
    'Start the thread to record on
    Record.Start(Me,"Recording",Null)

Ok ! got it....

A few other questions about the code, just to understand and be able to find the problem.....

B4X:
If Interrupt Then Exit

Where is this Interrupt defined ? What changes the Interrupt to True ? I cannot find this in the documentation

B4X:
Record.RunOnGuiThread("Showvolume",Array As Object(Sum))

If I understand, this sub ShowVolume has to be run on the GUI thread to be able to change the value of a label on the activity (screen) ?

In the log tab all of a sudden this is written:

B4X:
sending message to waiting queue (showvolume)
sending message to waiting queue (rec_ended)
running waiting messages (2)


What is logging this ? the thread ?
When I run the app by tapping on the phone icon after I quit the app it does not log this ?

thank you
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Where is this Interrupt defined
In the artest example, the only place the interrupt is set to true is when the user presses the back button to make sure that the recording stops before the pp closes. This is because it is only recording 10 seconds of audio, there is no stop button.

In the Dictaphone app, the interrupt is set when the user presses the stop button vie a call to the Interrupt method of the Record class.

If I understand, this sub ShowVolume has to be run on the GUI thread to be able to change the value of a label on the activity (screen) ?
Yes, you cannot change a view from a thread other than the GUI thread. I did not add this to the dictaphone app as I thought it would not be needed.

sending message to waiting queue (showvolume)
sending message
to waiting queue (rec_ended)
running waiting messages (
2)
These are written by the threading library.
 
Upvote 0

Olivier Zeegers

Member
Licensed User
In the artest example, the only place the interrupt is set to true is when the user presses the back button to make sure that the recording stops before the pp closes. This is because it is only recording 10 seconds of audio, there is no stop button.

In the Dictaphone app, the interrupt is set when the user presses the stop button vie a call to the Interrupt method of the Record class.


Yes, you cannot change a view from a thread other than the GUI thread. I did not add this to the dictaphone app as I thought it would not be needed.


These are written by the threading library.

Thank you, I understand more now...

This is the log when app is started from B4A.

B4X:
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
permission granted
Pos 40
Recording...
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
ReadBytes
WriteBytes
RunOnGuiThread
false
** Activity (main) Pause, UserClosed = false **
ReadBytes
WriteBytes
RunOnGuiThread
true
sending message to waiting queue (showvolume)
sending message to waiting queue (rec_ended)
running waiting messages (2)
Ended true
main_rec_ended (java line: 611)
java.lang.IllegalStateException: stop() called on an uninitialized AudioRecord.
    at android.media.AudioRecord.stop(AudioRecord.java:1059)
    at stevel05.audiorecord.AudioRecording.Stop(AudioRecording.java:224)
    at com.stevel05.artest.main._rec_ended(main.java:611)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
    at anywheresoftware.b4a.BA$2.run(BA.java:365)
    at anywheresoftware.b4a.BA.setActivityPaused(BA.java:437)
    at com.stevel05.artest.main$ResumeMessage.run(main.java:306)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6753)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:482)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
java.lang.IllegalStateException: stop() called on an uninitialized AudioRecord.
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **

It reads bytes and writes to the file a few times (10 ?) and then suddenly the Interrupt gets raised and it goes to Rec_ended where the Stop command gives an error on AudioRecord...

Does this ring a bell ?

It is after this comes into the log:

** Activity (main) Pause, UserClosed = false **

where that comes from I don't know
 
Upvote 0

Olivier Zeegers

Member
Licensed User
I can work with it when I run the app from B4A. Then close the app on my Phone and start it again from the Phone. Then everything works just fine ;-)

Thank you so much for the example !
Super to start from and modify to my needs....

again a question:

I want the Play button to change into a Pause button when the user presses Play and the app starts playing..
So I added to the btnPlay_Click:

B4X:
If PlayBack1.IsRunning = True Then
        btnPlay.Text=""
        PlayBack1.Stop
End If

This works but does not update the GUI and so I have to use RunOnGuithread right ?

I created a Public Sub in the Playback class:

B4X:
Public Sub SetPauseButton as Boolean
    PlayThread.RunOnGUIThread("SetPause", null)
End Sub

and an extra Sub in that same class for setting the icon:

B4X:
Public Sub SetPause
        btnPlay.Text=""
        PlayBack1.Stop
End Sub

Here I have to DIM the label ?

And in the code of btnPlay_Click:

B4X:
If PlayBack1.IsRunning = True Then
    PlayBack1.SetPauseButton
End If


is this right ? Seems to be a little complicated ?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Anything Gui related should be done in the Main module. If you change the gui from the record or playback modules, it will get complicated quickly. You can access the status of both modules from the main module and act accordingly.

When the play button is clicked, change the icon. It doesn't need to be done in the Playback Module.
 
Upvote 0

Olivier Zeegers

Member
Licensed User
Anything Gui related should be done in the Main module. If you change the gui from the record or playback modules, it will get complicated quickly. You can access the status of both modules from the main module and act accordingly.

When the play button is clicked, change the icon. It doesn't need to be done in the Playback Module.

OK, got this working more or less....

Another question:

I want to display a timer that runs while recording or playing as well as a seekbar.
During recording or playback I have to update the timer and the position of the seekbar.
When recording the app is looping the recordDo sub in record class but from there I cannot update the GUI ?

Where should I place my commands to update the seekbar ?

thank you

Olivier
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I would add these two subs to the playback module:

B4X:
Public Sub getFileSize As Long
    Try
        Return InFile.Size
    Catch
        Return 0
    End Try
End Sub

Public Sub getFilePos As Long
    Try
        Return InFile.CurrentPosition
    Catch
        Return 0
    End Try
End Sub

They are in try blocks as the RandomAccesFile object does not have an isInitialized method, and it will undoubtedly fail of you call it on an uninitialized file.

Then create a timer in the Main module running once a second should be enough. You can update the seekbar from the Timer_Tick sub.

The timer label can be also updated from the Timer_Tick sub, total play time in seconds should be FileSize / SampleRate, as there is no header on the temporary files used.
Play time in seconds should be FilePos / SampleRate.

You cannot update the slider on recording as you don't know how long the recording will last, you could create an indeterminate progressbar, but that wouldn't really give more indication than turning the record button red.
 
Upvote 0
Top