轉自http://blog.chinaunix.net/u1/49717/showart_2180166.html
?
如果幾年前你告訴我人們可以通過晃動手機或向麥克吹氣使手機有所動作,我一定會大笑不止。但現(xiàn)在這已經是事實了。
?
檢查晃動動作是很直接的,所有這些在3.0“motion event”(動作事件)中都有介紹。
?
檢測向麥克吹氣困難一點。本教程將建立一個簡單的單視圖程序,它將在用戶向麥克吹氣時向控制臺寫入記錄信息。
?
?
源代碼/Github
?
教程源代碼 可從GitHub獲得。 你可以克隆軟件倉庫或直接下載 zip文件 。
?
概述
?
檢測向麥克吹氣的工作可分為兩部分:(1) 獲取麥克輸入 (2) “聽”吹氣的聲音。
?
我們將使用3.0中新的AVAudioRecorder類來捕獲麥克輸入。使用AVAudioRecorder可以讓我們使用Objective-C,而不需像其他方法一樣使用C。
?
向麥克吹氣的噪聲/聲音是由低頻聲音組成的。我們將使用 low pass filter(低頻濾波) 來降低來自麥克的高頻聲音;當濾波信號的電平等級突然增大時,我們就知道有人向麥克吹氣了。
?
創(chuàng)建項目
?
啟動Xcode創(chuàng)建一個View-Based iPhone程序,叫MicBlow:
- 使用Xcode菜單 File > New Project… 創(chuàng)建一個新項目
- 從 iPhone OS > Application 選擇 View-based Application s然后按 Choose…
- 將項目命名為 MicBlow, 按 Save
?
添加AVFoundation Framework
?
為使用AVAudioRecorder類,我們需要向項目添加AVFoundation framework:
- 在項目 Groups & Files 面板上展開 Targets
- 按Control-點擊或右擊 MicBlow
- 選擇 Add > Existing Frameworks…
- 按下 Linked Libraries 左下角的 + 按鈕
- 選擇 AVFoundation.framework 并按下 Add
- AVFoundation.framework 出現(xiàn)在 Linked Libraries 下。關閉窗口
?
然后,我們在view controller接口中引入AVFoundation頭文件并設置AVAudioRecorder實例變量:
- 展開項目 Groups & Files 面板下的 MicBlow
- 展開 Classes 文件夾
- 選擇 MicBlowViewController.h 進行編輯
- 更新文件。修改見如下2,3,7行:
1
2 3 4 5 6 7 8 9 |
#import <UIKit/UIKit.h> ?
#import <AVFoundation/AVFoundation.h> ? #import <CoreAudio/CoreAudioTypes.h> ?
@interface
MicBlowViewController
:
UIViewController
|
?
引入CoreAudioTypes頭文件實際上是下一步需要的工作。我們還需要在設置AVAudioRecorder定義更多的常量。
?
獲取麥克輸入
?
我們在ViewDidLoad進行設置并開始“聽“取麥克:
- 解除樣本 ViewDidLoad 方法注釋
- 更新如下。見4-18行:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
-
(
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 :& amp;error ] ; ? ? ?? ? ? if ( recorder ) { ? ? ? ? ? ? ? ? [ recorder prepareToRecord ] ; ? ? ? ? ? ? ? ? recorder.meteringEnabled = YES ; ? ? ? ? ? ? ? ? [ recorder record ] ; ? ?? ? ? } else ? ?? ? ? ? ? ? ? NSLog ( [ error description ] ) ; ? ? } |
?
AVAudioRecorder的主要功能就像前名字暗示的那樣進行音頻錄制。其第二個功能是提供音頻電平等級信息。所以,這里我們只是將音頻輸入指向 /dev/null 位 – 我沒有找到任何文檔支持我的觀點,但一致意見是就像在任何Unix下一樣,/dev/null將打開音頻計量表。
?
注意: 如果你準備采用上述代碼,記住在設置meteringEnabled屬性或音頻計量開始工作前,要調用prepareToRecord (或者record)。
?
記住在dealloc中釋放recorder。 見第三行:
1
2 3 4 5 |
?
-
(
void
)
dealloc
? { ? ? ? ? ? [ recorder release ] ; ? ? ? [ super dealloc ] ; ? ? } |
?
音頻采樣
?
我們將使用定時器每秒30次檢查一次音頻電平等級。NSTimer實例變量以及其回調函數(shù)在MicBlowViewController.h中定義。修改見7,10行:
1
2 3 4 5 6 7 8 9 10 11 12 |
?
更新.m文件中ViewDidLoad啟用定時器。修改見16,17行:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-
(
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 :& amp;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 ] ) ; ? ? ? } |
?
現(xiàn)在,我們只是直接進行音頻采樣而未使用濾波。在.m文件中添加levelTimerCallback:
1
2 3 4 5 |
-
(
void
)
levelTimerCallback
:
(
NSTimer
*
)
timer
{
?
? ? [ recorder updateMeters ] ; ?? ? ? NSLog ( @ "Average input: %f Peak input: %f" , ? ? ? ? [ recorder averagePowerForChannel : 0 ] , [ recorder peakPowerForChannel : 0 ] ) ; ? } |
?
發(fā)送updateMeters消息來刷新平均和峰值功率。此計數(shù)是以對數(shù)刻度計量的,-160表示完全安靜,0表示最大輸入值。
?
不要忘記在dealloc中釋放定時器。修改見第三行:
1
2 3 4 5 6 |
-
(
void
)
dealloc
{ ? ? ? <strong> [ levelTimer release ] ;< / strong> ? ? [ recorder release ] ; ? ?? ? ? [ super dealloc ] ; ? } |
?
”聆聽“吹氣聲
?
正如概述中提到的那樣,我們要使用低通濾波來消除高頻聲音對電平帶來的影響。該算法建立了一系列將過去的每個采樣輸入合成而得到的結果。我們需要一個實例變量來保存此結果。更新.h文件。修改見第八行:
1
2 3 4 5 6 7 8 9 |
#import <UIKit/UIKit.h> ?
#import <AVFoundation/AVFoundation.h> ? #import <CoreAudio/CoreAudioTypes.h> ? ? ?
@interface
MicBlowViewController
:
UIViewController
{
?
|
?
替換levelTimerCallback:方法來實現(xiàn)此算法:
1
2 3 4 5 6 7 8 9 |
-
(
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 ) ; ? } |
?
我們在每次定時器回調時重新計算一次lowPassResults變量。為方便,我們將其轉換為0-1,0代表完全安靜,1代表最大音量。
?
但低通濾波值超過一定門檻范圍時,我們就可以判斷有人向麥克吹了氣。門檻范圍值的設定是一種技巧。它設定太小,則太容易被觸發(fā),如果設定太高,則必須長時間用盡力氣吹氣才會有效果。在我們的程序中,我將其設為0.95。我們要改變一下log的條件,見第6,7行:
1
2 3 4 5 6 7 8 |
-
(
void
)
listenForBlow
:
(
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" ) ; } |
?
好了!你可以檢測是否有人吹了麥克了。
?
鳴謝及說明
?
此方法在大部分情況下工作良好,但并非任何情況都正確。我是在飛行中寫的這篇文章,飛機的引擎聲經常觸發(fā)我的算法。類似地,在一個噪聲很大的房間內足夠多的低頻聲也會觸發(fā)我的算法。
?
算法節(jié)選自 this Stack Overflow post 。上面帖子使用的是 SCListener 庫來進行音頻電平檢測。SCListener比AVAudioRecorder更早出現(xiàn);它是用來隱藏C語言細節(jié)的獲取音頻電平代碼。而無疑AVAudioRecorder更容易使用。
?
最后,此方法確實可以在模擬器中正常工作。但你要找到Mac上的麥克。出乎我的意料,第一代Macbook上的麥克處于攝像頭左方的小孔中。
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
