数年前に書いた下記記事が古くなってきたので、Swift編 として書き直しました。
Core Audioを使用して、マイクから音を検知する方法をメモしておきます。
実行環境
- Xcode 7.3
- Swift 2.2
- Deployment Target iOS 8
実装方法
ヘッダをインポートします。
import AudioToolbox
音声入力用のキューと監視タイマーを準備しておきます。
var queue: AudioQueueRef!
var timer: NSTimer!
記録するデータフォーマットを決めます。
var dataFormat = AudioStreamBasicDescription(
mSampleRate: 44100.0,
mFormatID: kAudioFormatLinearPCM,
mFormatFlags: AudioFormatFlags(kLinearPCMFormatFlagIsBigEndian |
kLinearPCMFormatFlagIsSignedInteger |
kLinearPCMFormatFlagIsPacked),
mBytesPerPacket: 2,
mFramesPerPacket: 1,
mBytesPerFrame: 2,
mChannelsPerFrame: 1,
mBitsPerChannel: 16,
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 |
? |
音の監視を開始します。
var audioQueue: AudioQueueRef = nil
var error = noErr
error = AudioQueueNewInput(
&dataFormat,
AudioQueueInputCallback,
UnsafeMutablePointer(unsafeAddressOf(self)),
.None,
.None,
0,
&audioQueue)
if error == noErr {
self.queue = audioQueue
}
AudioQueueStart(self.queue, nil)
今回は音を検知するだけで、録音の必要はないので AudioInputCallback
は空にしておきます。
private func AudioQueueInputCallback(
inUserData: UnsafeMutablePointer<Void>,
inAQ: AudioQueueRef,
inBuffer: AudioQueueBufferRef,
inStartTime: UnsafePointer<AudioTimeStamp>,
inNumberPacketDescriptions: UInt32,
inPacketDescs: UnsafePointer<AudioStreamPacketDescription>)
{
}
マイクのレベルを取得できるように、レベルメータを有効化しておきます。
var enabledLevelMeter: UInt32 = 1
AudioQueueSetProperty(self.queue,
kAudioQueueProperty_EnableLevelMetering,
&enabledLevelMeter,
UInt32(sizeof(UInt32)))
一定時間ごとにマイクレベルを監視します。ここでは NSTimer
を使用しました。
self.timer =
NSTimer.scheduledTimerWithTimeInterval(0.5,
target: self,
selector: #selector(AudioViewController.detectVolume(_:)),
userInfo: nil,
repeats: true)
self.timer.fire()
一定時間ごとにレベルを取得して、画面に表示します。
func detectVolume(timer: NSTimer)
{
var levelMeter = AudioQueueLevelMeterState()
var propertySize = UInt32(sizeof(AudioQueueLevelMeterState))
AudioQueueGetProperty(
self.queue,
kAudioQueueProperty_CurrentLevelMeterDB,
&levelMeter,
&propertySize)
self.peakTextField.text =
"".stringByAppendingFormat("%.2f", levelMeter.mPeakPower)
self.averageTextField.text =
"".stringByAppendingFormat("%.2f", levelMeter.mAveragePower)
self.loudLabel.hidden = (levelMeter.mPeakPower >= -1.0) ? false : true
}
通常、mPeakPower
、mAveragePower
には負の値がセットされており、音量に比例して値も大きくなっていきます。最大値は 0 になります。
最後に忘れずにキューを空にして停止します。
AudioQueueFlush(self.queue)
AudioQueueStop(self.queue, false)
AudioQueueDispose(self.queue, true)
サンプルコード
注意点
- ユーザがマイクの使用を許可していない場合、音の検知はできません
応用できそうなアプリ
- 声や拍手などに反応するゲームアプリなど
- iPhoneに息を吹きかけると何かがめくれるアプリ(実際にありましたねw)
参考にさせて頂いたリンク