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

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