iPhone 的麥克風裝置是一個重要的輸入 Sensor ,在 iOS 3.0 後加入了 AVAudioRecorder Class ,我們可以利用 AVFoundation Framework來對此進行控制,在本篇教學文章中將介紹如何利用 AVAudioRecorder 搭配 NSTimer 來簡單的偵測輸入麥克風裝置的訊息。
首先加入 AVFoundation.framework 與 CoreAudio.framework,需要用裡面的 AVAudioRecorder Class 來偵測
接下來需要引入一些程式中會使用到類別
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
宣告一個 AVAudioRecorder 和 NSTimer
AVAudioRecorder *Recorder;
NSTimer *Timer;
宣告完成後我們要運用 AVAudioRecorder 中的一些物件和方法來進行實作
NSURL *url=[NSURL fileURLWithPath:@"/dev/null"];
//以下宣告的set可以參照Apple的Settings文件
NSDictionary *set = [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:set error:&error];
//若初始化成功則利用NSTimer來持續偵測聲音
if (Recorder) {
[Recorder prepareToRecord];
Recorder.meteringEnabled=YES;
[Recorder record];
Timer=[NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:@selector(TimerCallback:)
userInfo:nil repeats:YES];
}
else {
NSLog(@"error");
}
需要注意的是 [Recorder PrepareToRecord] 必須放在 Recorder.meteringEnable 之前。 再來要設定一個 TimerCallback 方法來執行 onTimer 的內容
-(void)TimerCallback:(NSTimer *)timer {
[Recorder updateMeters];
NSLog(@"平均:%f 波峰:%f",[Recorder averagePowerForChannel:0],[Recorder peakPowerForChannel:0]);
}
在使用 [Recorder averagePowerForChannel:0] 和 [Recorder peakPowerForChannel:0] 讀取 Power 的平均和波峰前必須先用 updateMeters 更新目前麥克風收到的訊息才行,而 Power 的數值會落在 -160 dB ~ 0 dB 之間。
請問此範例程式可以用在實機嗎?可以請問所謂的@"/dev/null"代表的含義是什麼呢?
回覆刪除Oliver Chen 您好:
刪除這範例就是在實機上執行的,我不確定模擬器能不能使用,畢竟麥克風輸入並不在電腦的標準配備裡。
@"/dev/null" 代表並未指明dev/下的那一個檔案,相關的資訊您可以參考官方的文件說明
https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioRecorder_ClassReference/Reference/Reference.html
謝謝牛奶,找了一下官方說明,再連結到*NSURL部份還是沒看到為什麼,還是這個意思『指向Device下的檔案,但未指明哪一個』的意思嗎?另一個問題是為什麼NSNumber numberWithFloat:44100.0,為什麼是44100這個值呢?
刪除抱歉,我有找到為何是44100這個值了,單聲軌 (Mono) 標準高傳真 (Hi-Fi) 音效:取樣頻率 44,100 Hz,
刪除您好:
刪除沒關係,我已鞥該把44100宣告成常數,就不會有這個誤會了。
路徑的問題,其實我不太確定,我猜它是需要一個絕對路徑,因為我們不想錄製下來,所以使用null,不過這樣翻譯有點奇怪位在官方文件中對於該NSURL的解釋是:
The file system location to record to. The file type to record to is inferred from the file extension included in this parameter’s value.
在使用其他路徑得結果反而沒辦法取得mic的相關數值。
另外不使用此方式來實作使用AudioQueueNewInput同樣也可以達到相同效果,但是實作的程式碼相對也比較複雜。
看一下原文說明:
刪除The primary function of AVAudioRecorder is, as the name implies, to record audio. As a secondary function it provides audio-level information. So, here we discard the audio input by dumping it to the /dev/null bit bucket — while I can’t find any documentation to support it, the consensus seems to be that /dev/null will perform the same as on any Unix — and explicitly turn on audio metering.
謝謝牛奶的說明,但是我很好奇你所說的原文說明,是你上幾層樓貼給我的官方說明連結嗎?以及你有回答實機測試,但資訊確輸出在console裡?可以這麼做嗎?console的波峰值變化,是哪裡的麥克風傳進去的呢?是iOS裝置傳的嗎?
刪除您好:
刪除原文來自http://mobileorchard.com/
http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/
console 與你是不是用實機測試沒有任何關係,只要你使用NSLOG,需席就會被傾印在console中,如果是實機測試當然是實機的mic收音,若是使用模擬器就要看mac本身有沒有連結mic。
謝謝牛奶的善心提供,讓我又看到一個好網站,可供學習!
刪除原來我一直以為console就是只能以模擬器的方式去做,原來也可以接著ios的裝置,再將ios收到的訊息再回傳到電腦內部,真的沒問都不知道,也謝謝你的說明。
牛奶,想請教你知道利用音源孔傳資料進去給iOS裝置的技術嗎?就像是ipod suffle可以利用音源孔再轉usb就可和itune同步了!我一直好奇這樣的技術怎麼完成的呢?!
刪除您好:
刪除資料本身就只有0與1,用什麼端子來傳送都無所謂阿,你看你的3.5接口,應該是3環4節的音源線,表示他可以有4種不同的表示方式,除了原先立體聲道佔用兩節,接地佔用一節,另一節通常是用來控制音量或是其他操作訊息的像是mic,不過實際的運作還是可以個別定義每個環節的作用。(單環雙結就一定是單聲道接地與單聲道)
回到傳輸面,只要讓某一節發出high或是low的訊號就可以進行資料的傳送。
再來是麥克風,只有high或是low當然不足以表示麥克風接收到的資訊,所以在電流實際上在傳送時會搭配 Pulse Width Modulation(PWM),想像成在high與low之間存在的曖昧數值,這是一個使用在類比訊號上的技巧。
我不確定這樣解釋你了不了解,因為在講下去就要講電子電路學了....
謝謝牛奶的回覆,
刪除1. 由你上述說法,那我從音源孔傳回去時,以上述程式而言,利用相關的avRecoder,只能解出dB訊號,又怎麼能解出所要的資料訊號呢?
2. 我今日試寫了一下程式後,我發現complier到實機時,對著iphone吹氣,nslog並無法呈現訊號變化,但是用模擬器時,對著macboo k的mic吹氣,nslog會有數據變化的反應,請問這問題會出在哪裡呢?
對不起,總是好多問題要請教。
您好:
刪除我手邊暫時沒有device可以測試,不過當初寫這篇文章的時候,是在手機上執行的沒錯,有可能是你的device沒有允許你的程式使用mic,你可以能要先針對這部份下手。
hi, 牛奶,過了一個年放了六天假,一如往常,還是個新手!但針對所謂的實機測試,今天終於試出來了,的確從iOS7後,要讀取使用mic,就要先做RequestRecordPermission的動作,因此不論在stackflow和對岸的博客都有相關的說明(台灣資訊好少= = )!
刪除如下所示的程式寫法:
if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {
[audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
if (granted) {
// Microphone enabled code
}
else {
// Microphone disabled code
}
}];
}
但困擾我的是原本以為加這段在viewdidload就可以,但還是一直進入模擬器可跑但實機還是沒反應的無限問題迴圈。
最後發現還得加入這段才行
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
這樣才能就能用實機測試了!
以上心得與您分享!謝謝你的幫忙。
我忘了說我有宣告 AVAudioSession *audioSession=[AVAudioSession sharedInstance];
刪除Oliver Chen 您好:
回覆刪除你比起去年的你已經厲害很多了,謝謝你的分享,感恩,過段時間我會把它整理進來。