Android Question help with Audiostreamer

IanMc

Well-Known Member
Licensed User
Longtime User
Hi all,

I'm making an app that will amongst other things allow the user to record a sound then play it back immediately.

Using Erel's example I have added a panel so that when you press the panel it starts recording and then when you release it, it plays back the sound.

This works fine on my tablet (Jellybean) with almost no delay but on my phone (gingerbread) it works fine most of the time but then there will be a delay of a few seconds before it plays back.

When I say 'almost no delay' there is still a delay... any way to make it snappier?

Also on my gingerbread phone it'll suddenly get noisy clicks in the audio so is there any way to re-initialise the sound hardware?

I include Erels project with the extra panel and you can download the apk directly to your device here:

Audiostreamer.apk

Also I find on my devices that I will often get a click or pop at the beginning and end of the recording so this altered routine:

B4X:
Sub btnStopRecording_Click
    Try
        streamer.StopRecording
    Catch
        Log(LastException.Message)
    End Try
    timer1.Enabled = False
    btnPlay.Enabled = True
    Label1.Text = ""
    Log(buffers.Size)
    buffers.RemoveAt(buffers.Size -1)
    buffers.RemoveAt(1)
    Log(buffers.Size)
End Sub

Butchers the buffers list to remove the first and last byte arrays which seems to clear up the noisy clicks.

Also for processing or examining the data before playing it back this altered routine:

B4X:
Sub btnPlay_Click
    btnStartRecording.Enabled = False
    streamer.StartPlaying
    'For Each b() As Byte In buffers
    For i = 0 To buffers.Size - 1
        Dim b2() As Byte = buffers.Get(i)
        'Log("b2.Length = " & b2.Length)
        streamer.Write(b2)
    Next
    streamer.Write(Null) 'when this "message" will be processed, the player will stop.
End Sub
breaks out each byte array from the list before sending it to the streamer.write method.
 

Attachments

  • AudioStreamer.zip
    8 KB · Views: 268

bparent

Member
Licensed User
Longtime User
The code comiles successfully and then I get this error message from the Logs:
** Activity (main) Create, isFirst = true **

java.lang.NegativeArraySizeException: -2

at anywheresoftware.b4a.audio.AudioStreamer.Initialize2(AudioStreamer.java:77)
at anywheresoftware.b4a.audio.AudioStreamer.Initialize(AudioStreamer.java:65)
at b4a.example.main._activity_create(main.java:305)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
at b4a.example.main.afterFirstLayout(main.java:98)
at b4a.example.main.access$100(main.java:16)
at b4a.example.main$WaitForLayout.run(main.java:76)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)


at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
java.lang.NegativeArraySizeException: -2


I am running V3.82 and Audio Version 1.63
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Ah, ok this is a bit worrying.
This means that if bparent tried my app it would crash.

It's easy enough to do a bunch of nested Try conditions (can you nest try conditions?)

This is the string used to initialize the Audiostreamer before recording:
B4X:
streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
What would be the best strings to use? perhaps in some order of quality versus reliability with 'snapiness' as another factor?
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
It would seem after further reading that it only works with 16 bit and a microphone is mono so the only real value we have to change is the sample rate (second value) where above it is set to 44100.

I have altered the code with alterations thanks to @Rick Harris and @Erel

The new code takes into account the errors that would otherwise occur if the app is paused while recording.

Here you will notice that I have made the first sample rate value 1024 something that will not work so the question is, what are the possible sample rate values?

I thought I read what they are somewhere but now I can't find it.

With this code you will see that it successfully steps through until it finds one that works which should make it a lot more compatible with a lot more devices if we can get the values right:
B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("1")
    If FirstTime Then
        buffers.Initialize
        timer1.Initialize("timer1", 1000)
    End If
End Sub

Sub streamer_RecordBuffer (Buffer() As Byte)
    'collect the recording data
    buffers.Add(Buffer)
End Sub

Sub ReleaseAudioStreamer    
  Dim r As Reflector
  r.Target = streamer
  r.Target = r.GetField("audioRecord")
  r.RunMethod("release")
End Sub

Sub btnStartRecording_Click
    Try
        ReleaseAudioStreamer
    Catch
        Log(LastException.Message)
    End Try

    'Try to initialise the Audiostreamer
    Try 'Option 1
        streamer.Initialize("streamer", 1024, True, 16, streamer.VOLUME_MUSIC)
     Log("Option 1 worked")
    Catch 'oops Option 1 didn't work
        Try 'Option 2
            streamer.Initialize("streamer", 22050, True, 16, streamer.VOLUME_MUSIC)
            Log("Option 2 worked")
        Catch 'oops Option 2 didn't work
            Try 'Option 3
                streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
                Log("Option 3 worked")
            Catch
                Log("I'm out of options!")
                ToastMessageShow("Failed to Initialize Record, it would seem that your device is not compatible", False)
            End Try 'Option 3
        End Try 'Option 2
    End Try    'Option 1

    buffers.Clear
    streamer.StartRecording
    recordingStart = DateTime.Now
    timer1.Enabled = True
    Timer1_Tick
    btnPlay.Enabled = False
End Sub

Sub Timer1_Tick
    Label1.Text = "Recording: " & _
        Round((DateTime.Now - recordingStart) / DateTime.TicksPerSecond) & " seconds"
End Sub

Sub btnStopRecording_Click
    Try
        streamer.StopRecording
    Catch
        Log(LastException.Message)
    End Try
    timer1.Enabled = False
    btnPlay.Enabled = True
    Label1.Text = ""
    Log(buffers.Size)
    buffers.RemoveAt(buffers.Size -1)
    buffers.RemoveAt(1)
    Log(buffers.Size)
End Sub

Sub btnPlay_Click
    btnStartRecording.Enabled = False
    streamer.StartPlaying
    'For Each b() As Byte In buffers
    For i = 0 To buffers.Size - 1
        Dim b2() As Byte = buffers.Get(i)
        'Log("b2.Length = " & b2.Length)
        streamer.Write(b2)
    Next
    streamer.Write(Null) 'when this "message" will be processed, the player will stop.
End Sub

Sub streamer_PlaybackComplete
    Log("PlaybackComplete")
    btnStartRecording.Enabled = True
End Sub

Sub streamer_Error
  Log("Error")
  Log(LastException)
End Sub
Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)
  ReleaseAudioStreamer 'this is to prevent the error that happens when recording and the app is sent to background
End Sub

Sub Panel1_Touch (Action As Int, X As Float, Y As Float)
    If Action = 0 Then
        btnStartRecording_Click
    Else If Action = 1 Then
        btnStopRecording_Click
        btnPlay_Click
        Dim byteCount As Int
        For Each b() As Byte In buffers
            byteCount = byteCount + b.Length
        Next
'        Log(buffers.Size & " Byte Arrays")
'        Log(byteCount & " Bytes")
        byteCount = 0
    End If
End Sub
 
Last edited:
Upvote 0

bparent

Member
Licensed User
Longtime User
Well, of course, the Try Catch does catch the error. I tried changing the sampling rates and true to false. Even tried a different volume channel, but still have same error. I am trying this on a Samsung S4. I notice the latest version of Audio library seems to be Version 1.62(?) on the forum, but for B4A Version 3.82 I guess the Audio version is 1.63. Don't suppose this could be a problem. What version of Audio are you using?

Ah, ok this is a bit worrying.
This means that if bparent tried my app it would crash.

It's easy enough to do a bunch of nested Try conditions (can you nest try conditions?)

This is the string used to initialize the Audiostreamer before recording:
B4X:
streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
What would be the best strings to use? perhaps in some order of quality versus reliability with 'snapiness' as another factor?
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
I'm using version 1.63

Do you know what all of the allowed sampling rates are?
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
1024 ain't a valid value anyway, try one of these 8000,11025,16000,22050,44100. not sure which ones android supports in that stream stuff tho.
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Thanks Sorex

I did this handsome nest of vipers and on both my devices it reports that Sample rate of 8000 worked
(you can hear it sounds a bit tinny and the removing of the first and last byte arrays clips it a bit)

Usually I'd think that users would want to check the other way around, largest sample rate first to lowest sample rate but I wanted to test it.
B4X:
Sub btnStartRecording_Click
    Try
        ReleaseAudioStreamer
    Catch
        Log(LastException.Message)
    End Try

    'Try to initialise the Audiostreamer 
    Try 'Option 1
        streamer.Initialize("streamer", 8000, True, 16, streamer.VOLUME_MUSIC)
     Log("Sample Rate of 8000 worked")
    Catch 'oops Option 1 didn't work
        Try 'Option 2
            streamer.Initialize("streamer", 11025, True, 16, streamer.VOLUME_MUSIC)
            Log("Sample Rate of 11025 worked")
        Catch 'oops Option 2 didn't work
            Try 'Option 3
                streamer.Initialize("streamer", 16000, True, 16, streamer.VOLUME_MUSIC)
                Log("Sample Rate of 16000 worked")
            Catch
                Try 'Option 4
                    streamer.Initialize("streamer", 22050, True, 16, streamer.VOLUME_MUSIC)
                    Log("Sample Rate of 22050 worked")
                Catch
                    Try 'Option 5
                        streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
                        Log("Sample Rate of 44100 worked")
                    Catch
                        Log("I'm out of options!")
                        ToastMessageShow("Failed to Initialize Record, it would seem that your device is not compatible", False)
                        Return               
                    End Try 'Option 5
                End Try 'Option 4
            End Try 'Option 3
        End Try 'Option 2
    End Try    'Option 1

    buffers.Clear
    streamer.StartRecording
    recordingStart = DateTime.Now
    timer1.Enabled = True
    Timer1_Tick
    btnPlay.Enabled = False
End Sub
 
Upvote 0

bparent

Member
Licensed User
Longtime User
Ok, success. This worked for sampling rate of 8000 for Samsung S4.

Thanks Sorex

I did this handsome nest of vipers and on both my devices it reports that Sample rate of 8000 worked
(you can hear it sounds a bit tinny and the removing of the first and last byte arrays clips it a bit)

Usually I'd think that users would want to check the other way around, largest sample rate first to lowest sample rate but I wanted to test it.
B4X:
Sub btnStartRecording_Click
    Try
        ReleaseAudioStreamer
    Catch
        Log(LastException.Message)
    End Try
 
    'Try to initialise the Audiostreamer
    Try 'Option 1
        streamer.Initialize("streamer", 8000, True, 16, streamer.VOLUME_MUSIC)
     Log("Sample Rate of 8000 worked")
    Catch 'oops Option 1 didn't work
        Try 'Option 2
            streamer.Initialize("streamer", 11025, True, 16, streamer.VOLUME_MUSIC)
            Log("Sample Rate of 11025 worked")
        Catch 'oops Option 2 didn't work
            Try 'Option 3
                streamer.Initialize("streamer", 16000, True, 16, streamer.VOLUME_MUSIC)
                Log("Sample Rate of 16000 worked")
            Catch
                Try 'Option 4
                    streamer.Initialize("streamer", 22050, True, 16, streamer.VOLUME_MUSIC)
                    Log("Sample Rate of 22050 worked")
                Catch
                    Try 'Option 5
                        streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
                        Log("Sample Rate of 44100 worked")
                    Catch
                        Log("I'm out of options!")
                        ToastMessageShow("Failed to Initialize Record, it would seem that your device is not compatible", False)
                        Return              
                    End Try 'Option 5
                End Try 'Option 4
            End Try 'Option 3
        End Try 'Option 2
    End Try    'Option 1
 
    buffers.Clear
    streamer.StartRecording
    recordingStart = DateTime.Now
    timer1.Enabled = True
    Timer1_Tick
    btnPlay.Enabled = False
End Sub
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Cool!

Let's do the dance

come on everyone, do the dance

you know you want to :D

(I just did the dance :oops:)
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
8KHz is the typical voice sampling rate. Is it audio or speech you want to record and playback?

8 might sound kind of hollow on sound tho, 16 would be ideal and not to huge in memory/filesize.
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
I think you nailed it, some devices might not be able to do more than 8khz

Great then to be able to fall back on that.

My idea was mainly for voice but they would be able to stick a break out cable into the audio socket and plug it into a synth.

Who knows?

The human mind knows no limits.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
if you would like to go through some extra coding you could use your check routine to fill a select box so that people can select the frequency somewhere in an options panel.

then you make everyone happy (if it supports better freqs than 8KHz) :)
 
Upvote 0
Top