Android Question Noise detector by microphone

Lakhtin_V

Active Member
Licensed User
Longtime User
I want to make a noise detector. I need to periodically read the signal level from the microphone. Sound recording using the WAV MP3 standards is not important. It is not important for me to know what the microphone hears, the main thing is the noise level (sound volume). How can such a problem be solved?
 

emexes

Expert
Licensed User
Or are you trying to detect super-short sounds, like a clap or thunder or a gunshot, and averaging the audio level over 40 milliseconds is muffling it out?

Measuring the "audio level range" using MaxSample - MinSample will detect short sounds better.
 
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
Are you trying to reduce the amount of data being processed? Is the 44100 samples per second loading the CPU and slowing down other programs?

Or are you trying to make it respond faster? On my phone, I'm getting new data 24 times per second, which seems fast enough for a noise detector.

Or are you trying to detect super-short sounds, like a clap or thunder or a gunshot, and averaging the audio level over 40 milliseconds is muffling it out?
I want to make an application that fixes a knock on the door or the slam of a closed door. I want to try to control the time and moment when relatives return home :) Therefore, it is important to control the sound clap. Perhaps 1/25 of a second will be enough, until I tested and checked ...
 
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
Are you trying to reduce the amount of data being processed? Is the 44100 samples per second loading the CPU and slowing down other programs?

Or are you trying to make it respond faster? On my phone, I'm getting new data 24 times per second, which seems fast enough for a noise detector.

Or are you trying to detect super-short sounds, like a clap or thunder or a gunshot, and averaging the audio level over 40 milliseconds is muffling it out?
Test results. Sensitivity good responds to the smallest noise. But when I clap my hands or knock on the table :) the result is not always recorded. A short sound as I expected flies through our sieve. So you need to somehow change the settings.
 
Upvote 0

emexes

Expert
Licensed User
I want to make an application that fixes a knock on the door or the slam of a closed door. I want to try to control the time and moment when relatives return home :) Therefore, it is important to control the sound clap. Perhaps 1/25 of a second will be enough, until I tested and checked ...

Well, it took me a while to get the files off the phone. Eventually I gave up and moved to a tablet with an older and less paranoid Android.

This is knocking on the side of a wooden chest next to, but not touching, the desk that the tablet is on:

1665580768657.png



and this is closing and opening the door to the room, then knocking six times (per above), then closing and opening the door again:

1665581259530.png


and no, I don't know why opening the door is louder than closing it, and what the softer sounds are in-between - perhaps my footsteps?
 
Last edited:
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
The 30000 was just to slow down the sound level measurement/calculation/log rate. Otherwise the log queue fills up and there is massive lag between the microphone input and the log output.

30000 was an arbitrary choice - it means the sound level stuff is done every ~30000 bytes ie ~15000 samples ie about 3 times per second.

In your live realtime stuff, where you're not slowed down by debug mode or by the log speed, you'd do the sound level stuff on every received buffer, which on my phone is every 3584 bytes = 1792 samples = 44100/1792 times per second (mental math: 25 times per second)

I tried to experiment with the parameter 30000 surprisingly, it only works with this value of 30000, this is strange and probably somehow related to the peculiarities of using the Audio Streamer library or 30000 depends on the size of the buffer being processed
 
Upvote 0

emexes

Expert
Licensed User
I tried to experiment with the parameter 30000 surprisingly, it only works with this value of 30000, this is strange and probably somehow related to the peculiarities of using the Audio Streamer library or 30000 depends on the size of the buffer being processed

Well, we have a saying here that describes this situation of success-by-chance:

more ass than class

Not sure how that reads when translated back into your usual language. 🤣

I'd be very surprised if 29999 or 30001 didn't work fine too.

Also, the number is there twice - I know, I've been a very naughty boy - and probably best to change both of them so they match, but tbh it should work even if they're not.

B4X:
    If SlowDown > 30000 Then
        SlowDown = SlowDown - 30000
 
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
Well, we have a saying here that describes this situation of success-by-chance:

more ass than class

Not sure how that reads when translated back into your usual language. 🤣

I'd be very surprised if 29999 or 30001 didn't work fine too.

Also, the number is there twice - I know, I've been a very naughty boy - and probably best to change both of them so they match, but tbh it should work even if they're not.

B4X:
    If SlowDown > 30000 Then
        SlowDown = SlowDown - 30000
I tried to change the parameter 30000 significantly from 60000 to 15000 in the hope of getting a minimum response period but the program was hanging. Changing the parameter 30000 almost made no sense. I simply refused to slow down and decided to build the entire analysis on the basis of single MAX and MIN data. Now the task seems to be successfully solved, the only problem with long-term scanning (listening) after a long listening stage is memory overflow, and I was hoping to listen to the environment during the night (several hours), I probably need to somehow transfer the microphone scanning process to a service, but I don’t know yet how to do it.
 
Upvote 0

emexes

Expert
Licensed User
decided to build the entire analysis on the basis of single MAX and MIN data.
Yeah, that's probably simpler. Except maybe make it that you need say at least 10 samples of the ~1800 in a block to be above your threshold so that you don't get tricked by random spikes. Also, remember the audio samples are signed, so half of loud samples will be negative ie "under" your positive threshold.

the only problem with long-term scanning (listening) after a long listening stage is memory overflow

You're probably already on to this, but just in case... the first two things I'd try are:

- lower the sample rate, unless there is some reason that you need CD quality (cf phones are 8000 samples/second)
- only save the samples around when the loud noise trigger goes off, plus a few seconds before and after would be good

and the third and/or fourth things I'd try are:

- reduce the 16-bit samples to 8 bits (cf phones are 8-bit)
- compress using MP3 or similar

memory overflow
Memory overflow, or "disk drive" overflow? ie filesystem
 
Last edited:
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
I have no task to record a sound sample. It will be enough for me to fix the moment of the signal burst. I think it will be easy to save it to a text file or SQL light, BUT how to make the microphone listen for a long time will still have to work, since the problem of overflowing RAM still occurs.
 
Upvote 0

emexes

Expert
Licensed User
I have no task to record a sound sample. It will be enough for me to fix the moment of the signal burst. I think it will be easy to save it to a text file or SQL light, BUT how to make the microphone listen for a long time will still have to work, since the problem of overflowing RAM still occurs.

If you don't need to record the sound, then today is your lucky day 🍻 just delete this line (well, comment it out) :

B4X:
Private Sub streamer_RecordBuffer (Buffer() As Byte)
    buffers.Add(Buffer)

and your overflowing RAM problem will should disappear. 🤔
 
Upvote 0

emexes

Expert
Licensed User
Hey, looks like you're not the first person on this track:

I want to use audio to drive events in an app I'm making. At first I thought voice recognition, but I want it to work offline. I only need to drive one event though so voice recognition is overkill and so I was wondering if I could simply detect loud events using an audio library to detect a clap or a whistle.
Does anyone have any advice on where I might start looking for ideas on how to do that?
Thanks.

Then again, no doubt you've already seen this. But this post is written now, so just in case...
 
Upvote 0

emexes

Expert
Licensed User
good advice. But now it appears more often.


🤔 I will retry mine in the morning, see how long it runs for.


But I think I may have other problems with running an app for a long time on the phone I've got with me here. It is a recent Android version, and seems to be super-aggressive about power-saving.
 
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
If you don't need to record the sound, then today is your lucky day 🍻 just delete this line (well, comment it out) :

B4X:
Private Sub streamer_RecordBuffer (Buffer() As Byte)
    buffers.Add(Buffer)

and your overflowing RAM problem will should disappear. 🤔
The hoes gave good advice. But now the simplified version of the code does not work. It seems to me that the event of appearance in the information buffer requires standard processing and storage in the buffer. Otherwise, I will not understand why the code stopped working without writing the buffer.
Noise detector (audiostreamer):
#Region  Project Attributes
    #ApplicationLabel: Knock
    #VersionCode: 3
    #VersionName:
    #BridgeLogger: True
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private streamer As AudioStreamer
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
'    Dim buffers As List
    Dim Lbl As Button
    Dim btnStartRec As Button
    Dim btnStopRec As Button
    Private rp As RuntimePermissions
    Private recordingStart As Long    'was commented out... but why?
    Dim SoundLevelBar As ProgressBar

End Sub

Sub Activity_Create(FirstTime As Boolean)
'    buffers.Initialize
    Screen
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Screen

    Lbl.Initialize("None")
    Lbl.Color=Colors.LightGray
    Lbl.Gravity=Gravity.FILL
    Activity.AddView(Lbl, 20%x, 31%y, 60%x, 10%y)
    
    btnStartRec.Initialize("StartRec")
    btnStartRec.Color=Colors.Gray
    btnStartRec.Gravity=Gravity.FILL
    btnStartRec.Text="START"
    Activity.AddView(btnStartRec, 10%x, 2%y, 30%x, 10%y)
    
    btnStopRec.Initialize("StopRec")
    btnStopRec.Color=Colors.Gray
    btnStopRec.Gravity=Gravity.FILL
    btnStopRec.Text="STOP"
    Activity.AddView(btnStopRec, 50%x, 2%y, 30%x, 10%y)

    SoundLevelBar.Initialize("SoundLevelBar")
    Activity.AddView(SoundLevelBar, 10%x, 60%y, 80%x, 10%y)
    
End Sub

Private Sub StartRec_Click
    rp.CheckAndRequest(rp.PERMISSION_RECORD_AUDIO)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    If Result = False Then
        MsgboxAsync("No permission", "")
        Return
    End If
    If streamer.PlayerBufferSize = 0 Then
        streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
'        streamer.Initialize("streamer", 100000, True, 16, streamer.VOLUME_MUSIC)
    End If
'    buffers.Clear
    streamer.StartRecording
    
    recordingStart = DateTime.Now
    SoundLevelBar.Progress=25
End Sub
 
Upvote 0

emexes

Expert
Licensed User
I think those queue overflow errors were from AudioStreamer or Java, letting you know that it's still collecting audio but the receiver seems to not be accepting it and that it can only buffer up to a limit.

Or... I've noticed that strange things can happen when Logs are generated faster than they can be displayed.

Anyway, I started simplifying the Knock app by ripping out the recording stuff, and then one thing led to another and... well, give this a burl:
(if it plays up on you, first thing to try would be #BridgeLogger: False)

B4X:
#Region  Project Attributes
'    #ApplicationLabel: B4A Example
'    #VersionCode: 1
'    #VersionName:
'    'SupportedOrientations possible values: unspecified, landscape or portrait.
'    #SupportedOrientations: unspecified
'    #CanInstallToExternalStorage: False
   
    #ApplicationLabel: Knock
    #VersionCode: 1
    #VersionName:
    #BridgeLogger: True
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False  
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
   
    Private streamer As AudioStreamer
End Sub


Sub Globals
    'These global variables will be redeclared each time the activity is created.
    Dim SoundLevelLabel As Label
    Dim KnockLabel As Label
    Dim btnStartRec As Button
    Dim btnStopRec As Button
    Dim SoundLevelBar As ProgressBar
    Dim Tick0 As Long    'tick as at start of recording
    Dim StartNoiseTick As Long = 0    'tick as at start of noise
    Dim MaxNoiseSoundLevel As Int    'maximum sound level of current noise burst
   
    Dim NoiseSoundLevel As Int = 1200    '*** adjust to suit your situation ***
End Sub


Sub Activity_Create(FirstTime As Boolean)
    '''Activity.LoadLayout("Layout")
    Screen
End Sub


Sub Activity_Resume
End Sub


Sub Activity_Pause (UserClosed As Boolean)
End Sub


Sub Screen
    SoundLevelLabel.Initialize("None")
    SoundLevelLabel.Color=Colors.LightGray
    SoundLevelLabel.Gravity=Gravity.FILL
    SoundLevelLabel.Text="TIME"    '"Время"
    Activity.AddView(SoundLevelLabel, 10%x, 31%y, 80%x, 10%y)
   
    KnockLabel.Initialize("None")
    KnockLabel.Color=Colors.LightGray
    KnockLabel.Gravity=Gravity.FILL
    KnockLabel.Text="TIME"    '"Время"
    Activity.AddView(KnockLabel, 10%x, 51%y, 80%x, 10%y)
   
    btnStartRec.Initialize("StartRec")
    btnStartRec.Color=Colors.Gray
    btnStartRec.Gravity=Gravity.FILL
    btnStartRec.Text="START"
    Activity.AddView(btnStartRec, 10%x, 2%y, 30%x, 10%y)
   
    btnStopRec.Initialize("StopRec")
    btnStopRec.Color=Colors.Gray
    btnStopRec.Gravity=Gravity.FILL
    btnStopRec.Text="STOP"
    Activity.AddView(btnStopRec, 50%x, 2%y, 30%x, 10%y)
   
    SoundLevelBar.Initialize("SoundLevelBar")
    Activity.AddView(SoundLevelBar, 10%x, 81%y, 80%x, 10%y)
End Sub


Private Sub StartRec_Click
    Private rp As RuntimePermissions

    Log("StartRec_Click")
   
    rp.CheckAndRequest(rp.PERMISSION_RECORD_AUDIO)
    Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
    If Result = False Then
        MsgboxAsync("No permission", "")
        Return
    End If

    Tick0 = DateTime.Now
   
    streamer.Initialize("streamer", 44100, True, 16, streamer.VOLUME_MUSIC)
    Sleep(1)
    streamer.StartRecording
    Sleep(1)
   
End Sub


Private Sub StopRec_Click
    Log("StopRec_Click")
    streamer.StopRecording
    Sleep(1)
   
    'let user know what's going on
    SoundLevelLabel.Text = "Stopped"
End Sub


Private Sub streamer_RecordBuffer (Buffer() As Byte)
'    buffers.Add(Buffer)
'    Log(DateTime.Now & TAB & Buffer.Length)
   
    If Buffer.Length = 0 Then
        Return    'nothing to do
    End If
   
    Dim bc As ByteConverter
    bc.LittleEndian = True
    Dim Sample16() As Short = bc.ShortsFromBytes(Buffer)
   
    Dim ThisSample As Short = Sample16(0)
    Dim MinSample As Short = ThisSample
    Dim MaxSample As Short = ThisSample
    For I = 1 To Sample16.Length - 1    'note starts from 1 not 0, so that there is always a previous sample
        ThisSample = Sample16(I)
       
        If ThisSample < MinSample Then
            MinSample = ThisSample
        else if ThisSample > MaxSample Then
            MaxSample = ThisSample
        End If
    Next

    Dim SoundLevel As Int = MaxSample - MinSample    '0..65535 ie potentially larger than Short
    If SoundLevel <= 0 Then    'will never be < 0, but is no extra cost to play safe
        Dim SoundLevelLog As Float = 0
    Else
        Dim SoundLevelLog As Float = Logarithm(SoundLevel, 65535)
    End If
   
    SoundLevelLabel.Text = NumberFormat2(SoundLevelLog, 1, 3, 3, False) & TAB & SoundLevel
   
'    Dim Seconds As Float = (DateTime.Now - Tick0) / 1000
'  
'    Log(                                                       _
'        NumberFormat2(Seconds, 1, 1, 1, False)        & TAB & _
'        Buffer.Length                                 & TAB & _
'        MinSample                                     & TAB & _
'        MaxSample                                     & TAB & _
'        SoundLevel                                    & TAB & _
'        NumberFormat2(SoundLevelLog, 1, 3, 3, False)          _
'    )
   
    SoundLevelBar.Progress = SoundLevelLog * 95    's/b 100 but looks like microphone is peaking out
   
    If SoundLevel > NoiseSoundLevel Then
        If StartNoiseTick = 0 Then
            StartNoiseTick = DateTime.Now
            MaxNoiseSoundLevel = SoundLevel    'start tracking max sound level
        Else
            If SoundLevel > MaxNoiseSoundLevel Then
                MaxNoiseSoundLevel = SoundLevel
            End If
        End If
    Else
        If StartNoiseTick <> 0 Then
            Dim EndNoiseTick As Long = DateTime.Now
            Dim NoiseDurationMilliseconds As Int = EndNoiseTick - StartNoiseTick
           
            Log(                                 _
                NoiseDurationMilliseconds      & _
                " ms noise at "                & _
                DateTime.Date(StartNoiseTick)  & _
                " "                            & _
                DateTime.Time(StartNoiseTick)  & _
                " (sound level "               & _
                MaxNoiseSoundLevel             & _
                ")"                              _
            )
           
            KnockLabel.Text =                    _
                "Last noise was at "           & _
                DateTime.Time(StartNoiseTick)  & _
                " (sound level "               & _
                MaxNoiseSoundLevel             & _
                ")"
           
            StartNoiseTick = 0    'reset ready for next noise
        End If
    End If
End Sub


Sub streamer_Error
    Log(LastException)
End Sub
Log output:
87 ms noise at 10/17/2022 12:55:50 (sound level 30063)
71 ms noise at 10/17/2022 12:55:50 (sound level 44678)
114 ms noise at 10/17/2022 12:55:50 (sound level 33845)
74 ms noise at 10/17/2022 12:55:50 (sound level 35714)
202 ms noise at 10/17/2022 12:55:52 (sound level 44502)
204 ms noise at 10/17/2022 12:55:52 (sound level 34355)
131 ms noise at 10/17/2022 12:55:52 (sound level 52561)
44 ms noise at 10/17/2022 12:55:52 (sound level 39897)
43 ms noise at 10/17/2022 12:55:53 (sound level 31880)
45 ms noise at 10/17/2022 12:55:53 (sound level 55874)
43 ms noise at 10/17/2022 12:55:53 (sound level 59671)
85 ms noise at 10/17/2022 12:55:57 (sound level 65535)
175 ms noise at 10/17/2022 12:55:59 (sound level 54392)
244 ms noise at 10/17/2022 12:55:59 (sound level 61232)
160 ms noise at 10/17/2022 12:55:59 (sound level 61009)
159 ms noise at 10/17/2022 12:55:59 (sound level 65146)
159 ms noise at 10/17/2022 12:56:00 (sound level 54263)
71 ms noise at 10/17/2022 12:56:00 (sound level 1296)
117 ms noise at 10/17/2022 12:56:00 (sound level 2189)
129 ms noise at 10/17/2022 12:56:01 (sound level 1643)
203 ms noise at 10/17/2022 12:56:05 (sound level 10176)
248 ms noise at 10/17/2022 12:56:05 (sound level 7001)
43 ms noise at 10/17/2022 12:56:06 (sound level 1251)
131 ms noise at 10/17/2022 12:56:06 (sound level 3314)
45 ms noise at 10/17/2022 12:56:06 (sound level 1630)
45 ms noise at 10/17/2022 12:56:06 (sound level 1630)
131 ms noise at 10/17/2022 12:56:06 (sound level 4220)
159 ms noise at 10/17/2022 12:56:06 (sound level 4109)
203 ms noise at 10/17/2022 12:56:06 (sound level 2750)
1262 ms noise at 10/17/2022 12:56:07 (sound level 6729)
43 ms noise at 10/17/2022 12:56:09 (sound level 1867)
74 ms noise at 10/17/2022 12:56:09 (sound level 1244)
 
Upvote 0

emexes

Expert
Licensed User
Anyway, I started simplifying the Knock app by ripping out the recording stuff, and then one thing led to another and... well, give this a burl:

I just came back to my phone after two hours. Screen was black. Did a couple of knocks, then woke up screen and it the most recent noise time was recent, rather than two hours ago. Although maybe it heard me as I was waking it up. :rolleyes:
 
Upvote 0

emexes

Expert
Licensed User
I just came back to my phone after two hours. Screen was black. Did a couple of knocks, then woke up screen and it the most recent noise time was recent, rather than two hours ago. Although maybe it heard me as I was waking it up. :rolleyes:

Hmm, replaced the "last noise" status Label with a ListView. It looks like it doesn't register noises while screen is off ie phone goes to sleep. But test it yourself - I have been making stupid mistakes more often lately. :oops:

Simplest solution might be to have phone connected to charger and disable all power-saving sleepouts. Let us know how that works out. 🍻
 
Upvote 0

Lakhtin_V

Active Member
Licensed User
Longtime User
Hmm, replaced the "last noise" status Label with a ListView. It looks like it doesn't register noises while screen is off ie phone goes to sleep. But test it yourself - I have been making stupid mistakes more often lately. :oops:

Simplest solution might be to have phone connected to charger and disable all power-saving sleepouts. Let us know how that works out. 🍻
More influence is the version of SDK I use to compile 30 gives a good result runs stable. Everything below sometimes freezes especially when the smartphone falls asleep
 
Upvote 0
Top