Come rilevare un colpo nel microfono con unità e iOS
-
27-10-2019 - |
Domanda
Sono consapevole che ci sono diversi thread con lo stesso problema, ma non sono stato in grado di far funzionare la loro soluzione. Ho finito per creare questa classe:
Microcontroller.H
#import Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#import <UIKit/UIKit.h>
@interface MicroController : UIView < UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, AVAudioSessionDelegate > {
AVAudioRecorder *recorder;
NSTimer *levelTimer;
double lowPassResults;
}
- (void)levelTimerCallback:(NSTimer *)timer;
@end
Microcontroller.mm
#import "MicroController.h"
@implementation MicroController
- (id)init
{
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;
if ([recorder prepareToRecord] == YES){
[recorder record];
}else {
int errorCode = CFSwapInt32HostToBig ([error code]);
NSLog(@"Error: %@ [%4.4s])" , [error localizedDescription], (char*)&errorCode);
}
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.03 target: self selector: @selector(levelTimerCallback:) userInfo: nil repeats: YES];
} else
NSLog([error description]);
// input 'level' is in meter.mAveragePower
return self;
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 1.0; // 0.05f
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (lowPassResults > 0.55)
NSLog(@"Mic blow detected");
[recorder updateMeters];
NSLog(@"Average input: %f Peak input: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0]);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"initiated");
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: YES];
} else
NSLog([error description]);
}
- (void)dealloc {
[levelTimer release];
[recorder release];
[super dealloc];
}
@end
Ma non riesco a far funzionare il microfono. Ricevo solo questo output e il microfono non risponde mai:
Input medio: -120.000000 Input di picco: -120.000000
Qualche idea su cosa può essere sbagliato?
Grazie per l'aiuto !
Soluzione
Beh, non pensavo importante menzionare che stavo lavorando con Unity, ed era il colpevole.
Unità modifica alcune impostazioni, quindi è importante farlo quando si carica il tuo ViewController:
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayAndRecord
error: &setCategoryError];
if (setCategoryError) {
NSLog([setCategoryError description]);
}
Altrimenti un audio ordinatore perfettamente scritto non funzionerà correttamente. E questo è stato ... Problema risolto!
Questo bug è stato un dolore da rilevare, spero che questo messaggio aiuti chiunque altro nella stessa situazione.
Altri suggerimenti
OK, ecco il risoluzione: file "miccontroller.m"
#import "MicController.h"
static MicController *sharedListener = nil;
@implementation MicController
+ (MicController *)sharedListener {
@synchronized(self) {
if (sharedListener == nil)
[[self alloc] init];
}
return sharedListener;
}
- (void)dealloc {
//[sharedListener stop];
//[levelTimer release];
[recorder release];
[super dealloc];
}
#pragma mark -
#pragma mark Listening
- (void)listen {
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayAndRecord
error: nil];
NSURL *url = [NSURL fileURLWithPath:@"/dev/null"];
//kAudioFormatAppleIMA4
//kAudioFormatMPEG4AAC
/*
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
*/
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
/*
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
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];
} else
NSLog([error description]);
}
- (void)stop {
[recorder release];
}
/*
- (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");
NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
}
*/
#pragma mark -
#pragma mark Levels getters
- (Float32)averagePower {
[recorder updateMeters];
const double ALPHA = 0.7;
double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
return [recorder averagePowerForChannel:0];
//return lowPassResults;
//NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
}
- (Float32)peakPower {
[recorder updateMeters];
const double ALPHA = 0.7;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
return [recorder peakPowerForChannel:0];
//return lowPassResults;
//NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
}
#pragma mark -
#pragma mark Singleton Pattern
+ (id)allocWithZone:(NSZone *)zone {
@synchronized(self) {
if (sharedListener == nil) {
sharedListener = [super allocWithZone:zone];
return sharedListener;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init {
if ([super init] == nil)
return nil;
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
- (void)release {
// Do nothing.
}
- (id)autorelease {
return self;
}
@end
Basta aggiungere il codice
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayAndRecord
error: nil];
sopra de void "- (void) ascolta {"
Grazie. Ciao
Ho lo stesso problema, nel caso in cui utilizzo un Applerator in titanio. Nel simulatore la classe funziona correttamente, ma nel dispositivo no, input medio = -120 ecco il codice:
//
// MicController.h
// Mic
//
// Created by DekWilde on 10/26/11.
// Copyright 2011 DekWilde. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
@interface MicController : NSObject {
AVAudioRecorder *recorder;
//NSTimer *levelTimer;
double lowPassResults;
}
+ (MicController *)sharedListener;
- (void)listen;
- (void)stop;
//- (void)levelTimerCallback:(NSTimer *)timer;
- (Float32)averagePower;
- (Float32)peakPower;
@end
e MicController.M
#import "MicController.h"
static MicController *sharedListener = nil;
@implementation MicController
+ (MicController *)sharedListener {
@synchronized(self) {
if (sharedListener == nil)
[[self alloc] init];
}
return sharedListener;
}
- (void)dealloc {
//[sharedListener stop];
//[levelTimer release];
[recorder release];
[super dealloc];
}
#pragma mark -
#pragma mark Listening
- (void)listen {
NSURL *url = [NSURL fileURLWithPath:@"/dev/null"];
//kAudioFormatAppleIMA4
//kAudioFormatMPEG4AAC
/*
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
*/
NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
/*
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
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];
} else
NSLog([error description]);
}
- (void)stop {
[recorder release];
}
/*
- (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");
NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
}
*/
#pragma mark -
#pragma mark Levels getters
- (Float32)averagePower {
[recorder updateMeters];
const double ALPHA = 0.7;
double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
return [recorder averagePowerForChannel:0];
//return lowPassResults;
//NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
}
- (Float32)peakPower {
[recorder updateMeters];
const double ALPHA = 0.7;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
return [recorder peakPowerForChannel:0];
//return lowPassResults;
//NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
}
#pragma mark -
#pragma mark Singleton Pattern
+ (id)allocWithZone:(NSZone *)zone {
@synchronized(self) {
if (sharedListener == nil) {
sharedListener = [super allocWithZone:zone];
return sharedListener;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init {
if ([super init] == nil)
return nil;
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
//- (void)release {
// Do nothing.
//}
- (id)autorelease {
return self;
}
@end
La mia domanda è: dove devo mettere questo codice che hai pubblicato sulla soluzione di questo problema. . . Come posso risolvere questo problema?
Ora che Unity 3.5 ha aggiunto supporto per Microfono Input ti suggerisco di farlo in unità in modo da non dover mantenere il codice del cacao e semplificare il porto su Android/Web/nativo in futuro.
Ci sono già alcuni post utili al riguardo:
http://forum.unity3d.com/threads/123036-ios-microphone-input
http://forum.unity3d.com/threads/118215-blow-tection-(using-ios-microfono)