koogawa blog

iOS、Android、foursquareに関する話題

【Tips】iOSでマイクの音を検知する

2016.4.17 追記:Swift版を書きました

blog.koogawa.com


Core Audioを使用して、マイクから音を検知する方法をメモしておきます。

実装方法

ヘッダをインポートします。

#import <AudioToolbox/AudioToolbox.h>

音声入力用のキューと監視タイマーを準備しておきます。

@interface AudioViewController : UIViewController
{
    AudioQueueRef   _queue;     // 音声入力用のキュー
    NSTimer         *_timer;    // 監視タイマー
}

記録するデータフォーマットを決めます。

AudioStreamBasicDescription dataFormat;
dataFormat.mSampleRate = 44100.0f;
dataFormat.mFormatID = kAudioFormatLinearPCM;
dataFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
dataFormat.mBytesPerPacket = 2;
dataFormat.mFramesPerPacket = 1;
dataFormat.mBytesPerFrame = 2;
dataFormat.mChannelsPerFrame = 1;
dataFormat.mBitsPerChannel = 16;
dataFormat.mReserved = 0;

▼各項目の意味

データ型 メンバ名 意味
Float64 mSampleRate サンプリング周波数(1秒間のフレーム数)
UInt32 mFormatID フォーマットID(リニアPCM、MP3、AACなど)
UInt32 mFormatFlags フォーマットフラグ(エンディアン、整数or浮動小数点数
UInt32 mBytesPerPacket 1パケット(データを読み書きする単位)のバイト数
UInt32 mFramesPerPacket 1パケットのフレーム数
UInt32 mBytesPerFrame 1フレームのバイト数
UInt32 mChannelsPerFrame 1フレームのチャンネル数
UInt32 mBitsPerChannel 1チャンネルのビット数
UInt32 mReserved

音の監視を開始します。

AudioQueueNewInput(&dataFormat, AudioInputCallback, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_queue);
AudioQueueStart(_queue, NULL);

今回は音を検知するだけで、録音の必要はないので AudioInputCallback は空にしておきます。

static void AudioInputCallback(
                               void* inUserData,
                               AudioQueueRef inAQ,
                               AudioQueueBufferRef inBuffer,
                               const AudioTimeStamp *inStartTime,
                               UInt32 inNumberPacketDescriptions,
                               const AudioStreamPacketDescription *inPacketDescs)
{
}

マイクのレベルを取得できるように、レベルメータを有効化しておきます。

UInt32 enabledLevelMeter = true;
AudioQueueSetProperty(_queue, kAudioQueueProperty_EnableLevelMetering, &enabledLevelMeter, sizeof(UInt32));

一定時間ごとにマイクレベルを監視します。ここでは NSTimer を使用しました。

_timer = [NSTimer scheduledTimerWithTimeInterval:0.5
                                          target:self
                                        selector:@selector(detectVolume:)
                                        userInfo:nil
                                         repeats:YES];

一定時間ごとにレベルを取得して、画面に表示します。

- (void)detectVolume:(NSTimer *)timer
{
    // レベルを取得
    AudioQueueLevelMeterState levelMeter;
    UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState);
    AudioQueueGetProperty(_queue, kAudioQueueProperty_CurrentLevelMeterDB, &levelMeter, &levelMeterSize);

    // 最大レベル、平均レベルを表示
    self.peakTextField.text = [NSString stringWithFormat:@"%.2f", levelMeter.mPeakPower];
    self.averageTextField.text = [NSString stringWithFormat:@"%.2f", levelMeter.mAveragePower];
}

通常、mPeakPowermAveragePower には負の値がセットされており、音量に比例して値も大きくなっていきます。最大値は 0 になります。

最後に忘れずにキューを空にして停止します。

AudioQueueFlush(_queue);
AudioQueueStop(_queue, NO);
AudioQueueDispose(_queue, YES);

サンプルコード

注意点

  • ユーザがマイクの使用を許可していない場合、音の検知はできません

応用できそうなアプリ

  • 声や拍手などに反応するゲームアプリなど
  • iPhoneに息を吹きかけると何かがめくれるアプリ(実際にありましたねw)

参考にさせて頂いたリンク