上一篇博文介绍了一下AVAudioPlayer,本文分享一下我学习AVAudioRecoder录制音频的相关内容。
打开你的iPhone,自带一个录音功能的应用---语音备忘录,界面简洁,功能很给力。即时通信软件,发送语音消息应该是常用的功能,目前一些K歌App也有录音这个功能,所以在开发中AVAudioRecorder也是比较重要的,高效灵活的使用还是很有必要的。
我下载过几款很棒的音频应用,比如全民K歌,弹琴吧,我可以用弹琴吧调试我的吉他,这个功能很强大,我再也不用我的调音器了。还有全民K歌,这款软件最强大功能是评分,其中有个细节引起我的注意。根据我的声音的强度的高低绘制不同的曲线,我在思考这个功能是如何实现的。今天找到了答案,通过 Metering(声音计量),可以通过这个技术将声音信号转化,以可视化的形式呈现。
AVAudioRecorder 有两个方法返回声音的分贝值。
- (float)peakPowerForChannel:(NSUInteger)channelNumber; /* returns peak power in decibels for a given channel */
- (float)averagePowerForChannel:(NSUInteger)channelNumber; /* returns average power in decibels for a given channel */
分贝值有个计算公式应该是 db = 10lgM (M表示声音强度吧),目前我们通过上面的两个函数得到db,平均分贝,峰值分贝。
我们希望将前面得到的分贝值的结果转化我线性的0到1形式,这需要一个转化的方式,如果我们把一次计算的结果记录下来,那么下次的计算就不需要了,因此提供下面的一个方法
#define MIN_DB -60.0f
#define TABLE_SIZE 300
@implementation THMeterTable {
float _scaleFactor;
NSMutableArray *_meterTable;
}
- (id)init {
self = [super init];
if (self) {
float dbResolution = MIN_DB / (TABLE_SIZE - 1);
_meterTable = [NSMutableArray arrayWithCapacity:TABLE_SIZE];
_scaleFactor = 1.0f / dbResolution;
float minAmp = dbToAmp(MIN_DB);
float ampRange = 1.0 - minAmp;
float invAmpRange = 1.0 / ampRange;
for (int i = 0; i < TABLE_SIZE; i++) {
float decibels = i * dbResolution;
float amp = dbToAmp(decibels);
float adjAmp = (amp - minAmp) * invAmpRange;
_meterTable[i] = @(adjAmp);
}
}
return self;
}
float dbToAmp(float dB) {
return powf(10.0f, 0.05f * dB);
}
- (float)valueForPower:(float)power {
if (power < MIN_DB) {
return 0.0f;
} else if (power >= 0.0f) {
return 1.0f;
} else {
int index = (int) (power * _scaleFactor);
return [_meterTable[index] floatValue];
}
}
@end
该类的主要的功能是:
//It creates an internal table storing the precomputed db -> linear
// values.
其中关键是_meterTable 保存一个列表
-(float)valueForPower:(float)power
这个接口提供查询。
接着是界面上的转化,重新绘制,或许要用到定时器,进行轮询。可以考虑用这个CADisplayLink作为解决方案。参考下面这段代码
self.levelTimer = [CADisplayLink displayLinkWithTarget:self
selector:@selector(updateMeter)];
self.levelTimer.frameInterval = 5;
[self.levelTimer addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
/* Defines how many display frames must pass between each time the
* display link fires. Default value is one, which means the display
* link will fire for every display frame. Setting the interval to two
* will cause the display link to fire every other display frame, and
* so on. The behavior when using values less than one is undefined. *
我的理解:如果是默认,定时器的刷新率是屏幕的刷新同步,应该是每秒60次,如果设置frameInterval = 5,则刷新率应该是 60 / 5 = 12。
本文
完!