Detecting blowing on the iPhone microphone? - iphone

I am trying to detect when the user is blowing into the mic of an iPhone. Right now I am using the SCListener class from Stephen Celis to call
if ([[SCListener sharedListener] peakPower] > 0.99)
in an NSTimer. However, this returns true sometimes when I'm not blowing. Anyone have any sample code to check if the user is blowing into the mic?

I would recommend low-pass filtering the power signal first. There is always going to be some amount of transient noise that will mess with instantaneous readings; low-pass filtering helps mitigate that. A nice and easy low-pass filter would be something like this:
// Make this a global variable, or a member of your class:
double micPower = 0.0;
// Tweak this value to your liking (must be between 0 and 1)
const double ALPHA = 0.05;
// Do this every 'tick' of your application (e.g. every 1/30 of a second)
double instantaneousPower = [[SCListener sharedListener] peakPower];
// This is the key line in computing the low-pass filtered value
micPower = ALPHA * instantaneousPower + (1.0 - ALPHA) * micPower;
if(micPower > THRESHOLD) // 0.99, in your example
// User is blowing on the microphone

when run on iPhone, you should add the following code after [recorder prepareToRecorder]
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];

Use return as lowPassResults is bigger than 0.55. This is working fine:
-(void)readyToBlow1 { NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: #selector(levelTimerCallback1:) userInfo: nil repeats: YES];
} else
NSLog(#"%#",[error description]);
}
-(void)levelTimerCallback1:(NSTimer *)timer { [recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
double lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (lowPassResults > 0.55) {
lowPassResults = 0.0;
[self invalidateTimers];
NextPhase *objNextView =[[NextPhase alloc]init];
[UIView transitionFromView:self.view
toView:objNextView.view
duration:2.0
options:UIViewAnimationOptionTransitionCurlUp
completion:^(BOOL finished) {}
];
[self.navigationController pushViewController:objNextView animated:NO];
**return;**
}
}

http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/
this tutorial works fine with simulator but its not working in iphone there is no response from iphone mic

Try this It is working fine for me. Thanks #jinhua liao
- (void)viewDidLoad {
[super viewDidLoad];
lowPassResults = 0.0;
[self readyToBlow1];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: YES];
} else
NSLog([error description]);
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
NSLog(#"lowpassResult is %f",lowPassResults);
if (lowPassResults > 0.95){
NSLog(#"Mic blow detected");
[levelTimer invalidate];
}
}

Related

how to detect blow from mic not voice in iphone?

i'm using this code to detect blow.
but i cant get the blow. i got voice.
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.3 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: YES];
} else
NSLog(#"error %#",[error description]);
But got voice not blow.
You kind of have to play with the low pass results. Try this:
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
//NSLog(#"%f", lowPassResults);
if (lowPassResults > 0.55)
NSLog(#"Mic blow detected");
}

Detecting Blow through iPhone MIC in Cocos and then Performing animation on an image

I am working on an app which is related to the recipes.In this app there is a section where user can blow air through the mic and can change the image as well the content on it by using animation transition CurlDown.I am able to detect the blow by using the following code,
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target: self selector: #selector(levelTimerCallback:) userInfo:nil repeats:YES];
}
else{
// NSLog([error description]);
}
image =[[UIImageView alloc] init];
image.image =[UIImage imageNamed:#"Recipie.png"];
image.frame =CGRectMake(50, 100, 150, 200);
[self.view addSubview:image];
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
// NSLog(#"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
if (lowPassResults >0.055 )
{
NSLog(#"Mic blow detected");
[self changeFrame];
}
}
-(void)changeFrame
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:image cache:NO];
[UIView commitAnimations];
}
But my problem is,when i blow first time through the mic then image animated 5 to 7 times and when i blow second time then images animates 9 to 10 times,i want this animation single times on every blow detection.
Please suggest me how can i do this using this code or it will be better for me if somebody can share the code for this section.
I suspect the problem comes from the fact that you are calling levelTimerCallback each second. So, when a blow come in, for the whole duration of the blow your callback will make the image change.
A workaround to this would be using a BOOL flag:
#property (nonatomic) BOOL blowDetected;
In levelTimerCallback, you keep track of when a blow is detected and when it ends and you do change images only for new blows:
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (lowPassResults > 0.055)
{
NSLog(#"Blow detected with power: %f", lowPassResults);
if (!self.blowDetected) {
self.blowDetected = YES;
NSLog(#"Mic blow detected");
[self changeFrame];
}
} else {
NSLog(#"Blow not detected with residual power: %f", lowPassResults);
self.blowDetected = NO;
}
}
This should prevent multiple image changes for the same blow...
Now, this will work fine when between a blow and the next one you wait enough time so that the power currently detected by the mic decreases below the 0.055 threshold. This means that any blow occurring before that will be ignored.
To improve this, instead of simply filtering the signal, we could simply try and detect when the filtered value increases; so I would suggest the following implementation:
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
double currentLowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (currentLowPassResults > 0.055)
{
NSLog(#"Blow detected with power: %f", lowPassResults);
if (!self.blowDetected || currentLowPassResult > K * lowPassResults) {
self.blowDetected = YES;
NSLog(#"Mic blow detected");
[self changeFrame];
}
} else {
NSLog(#"Blow not detected with residual power: %f", lowPassResults);
self.blowDetected = NO;
}
lowPassResult = currentLowPassResults;
}
You could find an optimal value for K by doing some tests.
In the latter implementation:
when a blow is first detected, we change the image and go into mode "wait for blow to extinguish" (self.blowDetected == YES);
in mode "wait for blow to extinguish", we do not change images, unless we detect a new blow, characterized by the fact that we have a recording power significantly larger than the current level.
Use return as you get first lowpass results >0.55
I have solve the issue have a look.
-(void)readyToBlow1 {
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: #selector(levelTimerCallback1:) userInfo: nil repeats: YES];
}
else
NSLog(#"%#",[error description]);
}
- (void)levelTimerCallback1:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
//NSLog(#"lowPassResults= %f",lowPassResults);
if (lowPassResults > 0.55)
{
lowPassResults = 0.0;
[self invalidateTimers];
NextPhase *objNextView =[[NextPhase alloc]init];
[UIView transitionFromView:self.view
toView:objNextView.view
duration:2.0
options:UIViewAnimationOptionTransitionCurlUp
completion:^(BOOL finished) {
}
];
[self.navigationController pushViewController:objNextView animated:NO];
**return;**
}
}

Microphone code doesn't run on iPhone device

I am trying to run the following code on my device with no success. Although the code works perfectly on Simulator. I have been following this tutorial. It simply crash on device.
http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/
Code is:
#interface MicBlowViewController : UIViewController {
AVAudioRecorder *recorder;
NSTimer *levelTimer;
double lowPassResults;
}
- (void)levelTimerCallback:(NSTimer *)timer;
#end
.m file :
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder)
{
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 3
target: self
selector: #selector(levelTimerCallback:)
userInfo: nil
repeats: YES];
}
else
NSLog(#"%#", [error description]);
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (lowPassResults < 0.95)
NSLog(#"Mic blow detected");
}
Add those two lines of code after [recorder prepareToRecord ]
[recorder prepareToRecord];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];

How to display audio meter on an iPhone [duplicate]

This question already has an answer here:
How to measure noise or sound and displays in dB(A) in iphone?
(1 answer)
Closed 9 years ago.
I have an audio recorder and I was wondering if anyone has a code to show a audio meter (the bar that most of audio recorders have which shows the level of input audio).
Apple's SpeakHere example code includes a LevelView class which seems to be exactly what you are looking for.
Heres an example that calculates output and prints it:
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"]; // Your audio save path
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: YES];
}
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
NSLog(#"%f",(lowPassResults*100.0f));
}
Depending on the lowPassResults you can animate a view accordingly.

Differences in behavior of AVAudioRecorder in iOS 4.1 and iOS3.1.3

I want to play an audio when someone speaks in the iPhone's mic. This code works perfectly in iPhone 2G(OS 3.1.3), but when it is tested in iPhone 4, it doesnt work. I checked for API and method differences, but could not find any.
- (void)viewDidLoad {
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/Audio.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.delegate=self;
if (audioPlayer == nil)
NSLog(#" %# ", [error description]);
audioSession = [[AVAudioSession sharedInstance] retain];
[audioSession setActive:YES error: nil];
[super viewDidLoad]; }
The action event:
-(IBAction) micAction{
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: nil];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: NO];
} }
- (void)levelTimerCallback:(NSTimer *)t {
[recorder updateMeters];
if([recorder averagePowerForChannel:0]>-38){
if ([audioPlayer prepareToPlay]) {
[recorder stop];
[recorder release];
[levelTimer invalidate];
[self playSound];
}
}}
-(void)playSound{
[audioSession setCategory:AVAudioSessionCategoryPlayback error: nil];
if ([audioPlayer prepareToPlay])
[audioPlayer play]; }
To make sure that the audio is playing in iPhone 4, i'd also set up an IBAction event, which is working fine. Also, for testing I'd set up a label to display the value of
[recorder averagePowerForChannel:0
after the button is pressed. This label updates itself as per the timer in 2G, but in iPhone 4 it displays just one value on the click event.
Am unable to debug the behavior of the method in iPhone 4 since i dont have the device with me now.
Can anyone please point out what the error is?
Thanks
Have you tried saving to a file instead of to /dev/null?
Found the solution. Issue was in this line:
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: NO];
Since repeats was set to NO, the timer used to fire only once. So, the continuos monitoring of the averagePowerForChannel value could not be done. After setting it to YES, the code works perfectly.