Detect microphone volume (blowing into microphone)

In my application I would like to detect if a user is blowing into the microphone. I’d like to achieve this by recording the sound with the microphone and check the volume, knowing that this is far from perfect as any noise could trigger it after all.

I read the documentation regarding sound recording and tuner and tried to use it for my purposes. I also have been studying the SimpleTuner example coded shipped with the Corona SDK. But it seems to me that the values proved by the tuner are just not usable to detect the volume correctly.

I have tested an implementation with Objective-C that uses AVAudioRecorder, which works well. You can find the example here:
http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/

The AVAudioRecorder provides the method peakPowerForChannel, which can be used to detect the volume. It returns the measured power in decibels, ranging from -160 dB (lowest volume) to 0 dB (max volume).

Could someone from the Ansca team please explain, what number exactly is returned from the getTunerVolume() function? Is the function using the same method from AVAudioRecorder to detect the recorded volume on iOS devices?

The documentation for getTunerVolume says: Get the last calculated volume number, the mean squared value of the samples in the current audio buffer using sample values scaled to be in the range [-1…1]. To get a more meaningful range of values, convert this to a log scale using for example 10*math.log( volume ).

But I have no clue what that should mean exactly (especially the: mean squared value of the samples in the current audio buffer using sample values scaled to be in the range [-1…1]). The raw output value is changing between 0.4 and 0.6, but seems not really affected by any input on the microphone (in big contrast to the native Objective-C code I tested with).

Further it is stated that convert this to a log scale using for example 10*math.log( volume ). But in the SimpleTuner example code the factor 20 is used instead of 10 local v = 20\*math.log(r:getTunerVolume()). Apart from that on most websites I found regarding this topic they use log10 and not log2.

Again, could please someone from the Ansca dev team shed some light on this topic and clarify the documentation regarding the getTunerVolume function? Or did any of the developers here have success using the tuner for such a purpose?

Thanks,
Thomas

[import]uid: 8111 topic_id: 4708 reply_id: 304708[/import]

I Would like to get some info on this subject as well…
Anyone? [import]uid: 13553 topic_id: 4708 reply_id: 17958[/import]

Hi. I’m new to corona and am also wondering if a blow into the microphone is possible. [import]uid: 27060 topic_id: 4708 reply_id: 18409[/import]

Here is some code to demonstrate a way to use Corona to detect sound. All it does is change the color of some text and is very simple-minded but hopefully this will help you get started.

It keeps a running average so it can adapt to different background noise levels
Every frame, it

  • gets the sound level value
  • converts it to dB
  • compares each new value with the value from the last frame
  • if the difference is a big enough jump, it’s a sound onset
  • if the difference is a big enough drop, it’s a sound offset
  • updates the running average.
  
-- Create an object to access audio input features  
local r = media.newRecording()  
r:startRecording()  
r:startTuner()   
  
-- Create an object to hold display items  
local g = display.newGroup()  
  
-- Simple sound detector   
-- This just changes the color of a text label in response to a detected sound.  
-- There are two threshold quantities here that could be used to adjust the sensitivity,  
-- one is the amount of change that detects sound onset, the other is for sound offset.  
-- You could connect these to a UI element to allow the user to adjust the sensitivity.  
local soundDetector = display.newText( "............", 0, 0, nil, 22 )  
soundDetector:setReferencePoint( display.TopLeftReferencePoint)  
soundDetector:setTextColor( 255,255,255, 150 )  
soundDetector.x = display.contentWidth/2   
soundDetector.y = 0.8\*display.contentHeight   
local lastV = 0  
local threshold = {}   
threshold.pos = 2.5 -- adjust these  
threshold.neg = 1 -- to change sensitivity  
  
function soundDetector:enterFrame( event )  
 local v = r:getTunerVolume()  
 if v == 0 then  
 return  
 end  
 -- Convert RMS power to dB scale  
 v = 20 \* 0.301 \* math.log(v)  
 soundDetector.text = string.format("%4.2f", v - lastV )   
 if v \> lastV + threshold.pos then  
 -- Detected a level increase  
 soundDetector:setTextColor( 255,255,100, 255 )  
 lastV = v  
 else  
 if v \< lastV - threshold.neg then   
 -- Detected a level drop  
 soundDetector:setTextColor( 255,255,255, 150 )   
 lastV = v  
 else  
 -- Adapt the background level  
 -- Simple running average   
 lastV = 0.5 \* v + 0.5 \* lastV  
 end  
 end  
end  
Runtime:addEventListener( "enterFrame", soundDetector );  
g:insert(soundDetector)  

Note – this API currently only works on iOS and on Mac. Works great for detecting blowing on the mic.
[import]uid: 6787 topic_id: 4708 reply_id: 18449[/import]

Another limitation is that you can’t currently play sound back at the same time as you are recording. So no ocarinas or trumpets with Corona just yet. [import]uid: 6787 topic_id: 4708 reply_id: 18452[/import]

Thanks for the info. So the sound detection from the mic isn’t possible on Android using Corona? [import]uid: 27060 topic_id: 4708 reply_id: 18455[/import]

@ptotheaul - no, not yet on Android because sound recording doesn’t currently work. [edit] Actually I just tried this sample code on a Droid X and it does work! [import]uid: 6787 topic_id: 4708 reply_id: 18745[/import]

@snarla Ah. What version of android are you running? Could it have something to do with that? Wait! You’re staff! Can we figure out if this is now supported? [import]uid: 27060 topic_id: 4708 reply_id: 18769[/import]

Hi all,

I’m glad the thread finally gains traction. I did some testing with the code provided by Snarla (thanks again for that). I added another output text field for the raw input value from getTunerVolume and set the positive threshold down to 2 to get better result on my testing devices (iPhone 3G and iPod touch 4th gen), see the adjusted code below.

I did a lot of testing in completely different environment with the Corona code below and the program mentioned in my original post based on AVAudioRecorder from the Cocoa library (Objective-C). My observation is as follows and I’m curious if others have similar experience.

  • The code works in very silent environment with no sudden disturbing noise. The raw value is between 0.2 and 0.3 in that case.

  • If there is just some slight background noise (some running computers in office, typing/clicking sounds) the raw value is fluctuating a lot between 0.4 and 0.6 and it either no blow is recognized or many false once (no blowing, just some noise).

  • In a subway with people around (talking or not) and noise from the train itself the value is constantly around 0.5 to 5.8., so no blow can be detected.

  • If the background is really loud (I attended a concert and tested the program) the raw value is constantly around 0.5 to 0.6. The good thing is that it wasn’t fluctuating and thus not detecting wrong blows, but I really cannot understand why even in such a loud environment the value never grew bigger than around 0.6.

So from my experience it seems that the calculated value for getTunerVolume is way to sensitive. I also tested it on my mac book, there the input has much lower values compared to the iPhone, but also a lot of fluctuation from just office background noise (no people talking) and constantly detects wrong blows. Can anyone else confirm this behavior?

The values read from AVAudioRecorder behave much differently. Even in a subway with noisy people the peak value very seldom reaches 0 (which for AVAudioRecorder means highest volume). Only at the concert I got constantly the peak value 0.

@Snarla could you give us some details regarding getTunerVolume and your testing code? Please see my questions below.

  • What is the highest raw value you measure with an iOS testing device? I never saw any value beyond 0.62 no matter how loud the surrounding are or how much I blow into the microphone.

  • From what I read so far regarding sound pressure/level comparision (eg. http://www.animations.physics.unsw.edu.au/jw/dB.htm) the formula to compare 2 sound levels is as follows:

20 \* log10 \* (p2/p1)  

In your code you use not log with base 10 but the natural log (base e), but multiply with 0.301. Further you don’t compare 2 sound levels which is actually needed as dB describes the difference between two levels and is not an absoluate value. Can you explain why exactly you convert the volume in your code in that way?

  • You wrote that we currently cannot “play sound back at the same time as you are recording”. Does that affect only background music or any kind of short sound effect? For my current project I only have short sound effects, would it be possible to still use sound recording when no sound is played? E.g. is it possible before playing a sound to stop the recorder and start again after playing the sound (or does that affect performance too much)? That is actually a big bummer, and I was not aware of such limitation as it is not written anywhere in the API documentation.

[code]
– Create an object to access audio input features
local r = media.newRecording()
r:startRecording()
r:startTuner()

– Create an object to hold display items
local g = display.newGroup()

– Simple sound detector
– This just changes the color of a text label in response to a detected sound.
– There are two threshold quantities here that could be used to adjust the sensitivity,
– one is the amount of change that detects sound onset, the other is for sound offset.
– You could connect these to a UI element to allow the user to adjust the sensitivity.
local soundDetector = display.newText( “…”, 0, 0, nil, 22 )
soundDetector:setReferencePoint( display.TopLeftReferencePoint)
soundDetector:setTextColor( 255,255,255, 150 )
soundDetector.x = display.contentWidth/2
soundDetector.y = 0.8*display.contentHeight
local lastV = 0
local threshold = {}
threshold.pos = 2 – adjust these
threshold.neg = 1 – to change sensitivity

local rawOutput = display.newText( “…”, 0, 0, nil, 22 )
rawOutput:setReferencePoint( display.TopLeftReferencePoint)
rawOutput:setTextColor( 255,255,255, 150 )
rawOutput.x = display.contentWidth/2
rawOutput.y = 0.6*display.contentHeight

function soundDetector:enterFrame( event )
local v = r:getTunerVolume()

rawOutput.text = string.format(“raw %4.3f”, v)

if v == 0 then
return
end
– Convert RMS power to dB scale
v = 20 * 0.301 * math.log(v)
soundDetector.text = string.format("%4.2f", v - lastV )

if v > lastV + threshold.pos then
– Detected a level increase
soundDetector:setTextColor( 255,255,100, 255 )
lastV = v
else
if v < lastV - threshold.neg then
– Detected a level drop
soundDetector:setTextColor( 255,255,255, 150 )
lastV = v
else
– Adapt the background level
– Simple running average
lastV = 0.5 * v + 0.5 * lastV
end
end
end
Runtime:addEventListener( “enterFrame”, soundDetector );
g:insert(soundDetector)
[/code] [import]uid: 8111 topic_id: 4708 reply_id: 19181[/import]

I am getting the same issues here. Actually on the new ipod touch the volume just stays to the max. It never drops below .560.
It makes it unusable. It’s way too sensitive. [import]uid: 8192 topic_id: 4708 reply_id: 19429[/import]

Any fix for getting the microphone volume?
I’ve tried the demo and all the examples, also tried looking to the raw values but honestly I don’t understand if it’s just working at random or it’s too sensitive.

Since it’s a problem reported about 4 months ago I’m sure you’ve fixed, just can’t find the correct way to get volume value.

Please ansca, Carlos, everyone just explain us how the get a correct volume value, otherwise admit this feature is just not working
[import]uid: 9158 topic_id: 4708 reply_id: 30637[/import]

Hi Shedder,

I don’t think there has been any update on this issue. Generally they never acknowledged that there is something wrong with the tuner getVolume function or at least explain in detail why the function is behaving the way it does (again, from my experience it appears to be much too sensitive). Thus after spending a lot of time I simply had to give up on detecting microphone volume and moved on without this feature. It would be nice though if this will be fixed in a future version, but right now I’m not holding my breath.

Thomas [import]uid: 8111 topic_id: 4708 reply_id: 30903[/import]

Hi Teichmann,
I’m not even so sure it’s just too sensitive. On my iPhone 3G it looks completely random, if I’m in some place with no sounds or in the subway, the values jump around in the same way.
It’s useless. I mean, how can you possibly do something with that function?

It’s frustrating… I had a request from a client about making an app that needed to measure noise levels… I checked the Corona API and I found the getVolume so I told him, of course, I can do it.

Well… now I’m turning down that job, since the getVolume function is a joke. And I don’t understand why Ansca is not fixing it… I mean, it should not be that hard or time consuming!

BTW, I’ve already turned down another job because it needed a multi-line input textbox (not possible with Corona)… and the new UI library? it was “almost” ready months ago…

I like Corona, honestly, I also wrote about it on Ansca blog, but now I’m definitely considering starting to learn Obj C and switch to xCode since it’s just missing too many basic features.

It’s ok if you guys can’t add support of 3D or 2.5D, shaders or image overlay methods and filters… but at least getting the Mic Volume? The audio panning (no pitch, 3d effects… just left and right)? Multiline input boxes? Good listeners for maps? A way to communicate with web views (the workarounds are not practical at all)? Retina texts (native)?
Those are basic features…

I understand you’re working on fixing Android performance right now, but we, developers, should have access to Corona Roadmap. We need to know if you’re really working on a certain feature and an estimate on when it’s going to be ready.
We are your customers, but we also have clients, we need to know what we can do and when we’ll be able to offer a certain feature.

[import]uid: 9158 topic_id: 4708 reply_id: 31017[/import]

Hey does the code to detect sound work well for simulator. Because the values are continuously fluctuating whether there is any sound or not. I want to know how to get a more stable value for volume as I don’t want it to keep fluctuating. This volume value will be used as input for display function.

Please help guys. [import]uid: 44499 topic_id: 4708 reply_id: 32748[/import]

Hey does the code to detect sound work well for simulator. Because the values are continuously fluctuating whether there is any sound or not. I want to know how to get a more stable value for volume as I don’t want it to keep fluctuating. This volume value will be used as input for display function.

Please help guys. [import]uid: 44499 topic_id: 4708 reply_id: 32749[/import]

Hey did u find any solution to the problem. I am currently working on creating an app that requires to determine the volume. Also the value of the volume cant keep fluctuating. Please help. I need to create this app as part of my final thesis and I have less then 2 weeks to wrap it up this part is holding me back , would really appreciate if you could guide me.

Awaiting your reply :slight_smile: and thanks in advance. [import]uid: 44499 topic_id: 4708 reply_id: 32825[/import]

Actually, at least from my tests, getTunerVolume seems to work on Simulators (Corona and xCode), it’s probably too sensitive but it works.

The problem is that it doesn’t work on devices!

I mean, if I launch the SimpleTuner example on the simulator and make some noise, the values change accordingly… however if I build it and install on device (tested on iPhone 3G and iPad) the values are basically random… it’s basically useless.

I’ve submitted a bug report few days ago, let’s see… [import]uid: 9158 topic_id: 4708 reply_id: 32835[/import]

I just found this thread and I wanted to bump it up to the top again, as I thought it would be cool to try audio to control a player by using the Iphone4’s two mic’s to detect where the sound is coming from and move player accordingly.

This would work in just like how you use the accelerometer to tilt and move player.

Now, from I can read from the posts in this thread I could only assume that CoronaSDK is not “there” just yet to support this type of controls.

lano78 [import]uid: 13560 topic_id: 4708 reply_id: 36437[/import]

Is there a way in which I can get the recorded data to buffers (e.g. double buffering) instead of to a file.
I need to analyze the data, like in a spectrum analyzer.
Thanks
[import]uid: 77519 topic_id: 4708 reply_id: 46589[/import]

Hi folks,

It’s great to see so much interest in sound input in the Corona community!

Ansca may eventually be able to devote resources to developing more powerful audio analysis features, but for now we have to make do with what we have, i.e. the Tuner API (which was designed for pitch detection, not for general audio analysis).

In the code sample I provided, please take note of the threshold parameters and the inline comments. Most likely you *will* need to figure out a way to adjust them to work with your particular input device(s). Please also take notice of the simple running average used to compute the background level. This is another part we hope you will play with and adjust and improve for your own purposes.

A really good “blowing” detector should not just respond to volume increases and decreases, but also to the frequency content of the sound: ideally it should be able to tell the difference between blowing and speech, for example. This type of feature detection is not available in Corona at the moment.

The raw values from getTunerVolume() depend on the microphone, the device, and the testing environment and are NOT very useful without being converted to some kind of dB scale. So, don’t worry if they don’t seem to be changing very much. That is why this example is written to respond mainly to *changes* in the input. In my testing I’ve found that for some reason an iPod with a mic is noticeably less sensitive than a 3GS with its built-in mic.

@teichmann you are technically right about the log conversion, properly the factor should be log base 10 of e which is 0.434 or so, not 0.301 (oops). BUT you can just drop this factor altogether and save yourself some processor load, since this code is just detecting level changes and the threshold can be adjusted. Also the dB scale here is dBfs (dB relative to full scale, which is 1.0 in this case). Also, no this is not the same as what you get from AVAudioRecorder. The metering functions in AVAudioRecorder probably also perform additional processing.

[import]uid: 6787 topic_id: 4708 reply_id: 50073[/import]