iPhone microphone输入事件捕获_kaudioqueueproperty_currentlevelmeterdb-程序员宅基地

技术标签: null  【开发技术】IOS  session  audio  interface  iphone  数据结构  


目的:
利用麦克风做为一种事件的输入方式
核心:
通过AudioSession与AudioQueue实现麦克风输入的数据捕捉.

开启AudioSession:
1.    AudioSessionInitialize
2.    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory)
3.    AudioSessionSetActive

建立声音格式:
1.    声音格式的数据结构AudioStreamBasicDescription
2.    使用kAudioFormatLinearPCM来做为声音格式

建立AudioQueue:
1.    AudioQueueNewInput
2.    AudioQueueStart
3.    AudioQueueSetProperty(kAudioQueueProperty_EnableLevelMetering)

获取声音峰值数据:
1.    记录峰值的数据结构AudioQueueLevelMeterState
2.    AudioQueueGetProperty(kAudioQueueProperty_CurrentLevelMeterDB)

关闭AudioQueue:
1.    AudioQueueStop
2.    AudioQueueDispose

代码:


#import <UIKit/UIKit.h>
#include <AudioToolbox/AudioToolbox.h>

@interface MicrophoneTestViewController : UIViewController {

    IBOutlet UILabel*    _averagePower;
    IBOutlet UILabel*    _peakPower;

    AudioQueueRef                mQueue;
    AudioStreamBasicDescription    mFormat;
    AudioQueueLevelMeterState    *_chan_lvls;
    NSArray                        *_channelNumbers;
}

-(void)setChannelNumbers:(NSArray *)v;
-(void)initAudioSession;

- (IBAction)startstop: (id) sender;

@end
[/code]

[code]
#import "MicrophoneTestViewController.h"

static void MyInputBufferHandler(void *                                    inUserData,
                                 AudioQueueRef                            inAQ,
                                 AudioQueueBufferRef                    inBuffer,
                                 const AudioTimeStamp *                    inStartTime,
                                 UInt32                                    inNumPackets,
                                 const AudioStreamPacketDescription*    inPacketDesc)
{
    // 如果要记录声音,可以在这里做记录处理.
    // 如果要分析声音数据,可以在这里做记录处理.
}

static void interruptionListener(void *    inClientData,
                                 UInt32    inInterruptionState)
{
    // 声音中断通知(BEGIN,END)
}

@implementation MicrophoneTestViewController

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    _averagePower.text = @"0";
    _peakPower.text = @"0";
    mQueue = NULL;
    _channelNumbers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
    _chan_lvls = (AudioQueueLevelMeterState*)malloc(sizeof(AudioQueueLevelMeterState) * [_channelNumbers count]);

    [self initAudioSession];

    [NSTimer 
     scheduledTimerWithTimeInterval:1.f/30.f
     target:self 
     selector:@selector(_refresh) 
     userInfo:nil 
     repeats:YES
     ];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    [_channelNumbers release];
    free(_chan_lvls);
}


- (void)dealloc {
    [super dealloc];
}

-(void)initAudioSession
{
    OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, self);
    if (error) printf("ERROR INITIALIZING AUDIO SESSION! %d\n", (int)error);
    else 
    {
        UInt32 category = kAudioSessionCategory_PlayAndRecord;    
        error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
        if (error) printf("couldn't set audio category!");

        error = AudioSessionSetActive(true);
        if (error) printf("AudioSessionSetActive (true) failed");
    }
}

-(void)setupAudioFormat:(UInt32)inFormatID
{
    memset(&mFormat, 0, sizeof(mFormat));

    UInt32 size = sizeof(mFormat.mSampleRate);
    OSStatus result = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
                            &size, 
                            &mFormat.mSampleRate);

    size = sizeof(mFormat.mChannelsPerFrame);
    result = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, 
                            &size, 
                            &mFormat.mChannelsPerFrame);

    mFormat.mFormatID = inFormatID;
    if (inFormatID == kAudioFormatLinearPCM)
    {
        // if we want pcm, default to signed 16-bit little-endian
        mFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
        mFormat.mBitsPerChannel = 16;
        mFormat.mBytesPerPacket = mFormat.mBytesPerFrame = (mFormat.mBitsPerChannel / 8) * mFormat.mChannelsPerFrame;
        mFormat.mFramesPerPacket = 1;
    }
}

-(void)startMicrophone
{
    [self setupAudioFormat:kAudioFormatLinearPCM];
    OSStatus result = AudioQueueNewInput(&mFormat, MyInputBufferHandler, NULL, NULL, NULL, 0, &mQueue);
    if (result == noErr) {
        result = AudioQueueStart(mQueue, NULL);
        if (result == noErr) {
            UInt32 val = 1;
            AudioQueueSetProperty(mQueue, kAudioQueueProperty_EnableLevelMetering, &val, sizeof(UInt32));

            if (mFormat.mChannelsPerFrame != [_channelNumbers count])
            {
                NSArray *chan_array;
                if (mFormat.mChannelsPerFrame < 2)
                    chan_array = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
                else
                    chan_array = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], [NSNumber numberWithInt:1], nil];

                [self setChannelNumbers:chan_array];
                [chan_array release];

                _chan_lvls = (AudioQueueLevelMeterState*)realloc(_chan_lvls, mFormat.mChannelsPerFrame * sizeof(AudioQueueLevelMeterState));
            }

            return;
        }
    }

    // 失败
    mQueue = NULL;
    NSLog(@"startMicrophone:失败.");
    return;
}

-(void)stopMicrophone
{
    if (mQueue) {
        AudioQueueStop(mQueue, true);
        AudioQueueDispose(mQueue, true);
        mQueue = NULL;
    }
}

-(void)_refresh
{
    if (mQueue) {
        UInt32 data_sz = sizeof(AudioQueueLevelMeterState) * [_channelNumbers count];
        OSErr status = AudioQueueGetProperty(mQueue, kAudioQueueProperty_CurrentLevelMeterDB, _chan_lvls, &data_sz);
        if (status == noErr)
        {
            // 这里没有去处理多个通道的数据显示,直接就显示最后一个通道的结果了
            // 这里的值就是我们打算用来做为一些触发机制的值了,需要用到的时候直接访问_chan_lvls这个数组
            for (int i=0; i<[_channelNumbers count]; i++)
            {
                NSInteger channelIdx = [(NSNumber *)[_channelNumbers objectAtIndex:i] intValue];
                if (channelIdx < [_channelNumbers count] && channelIdx <= 127)
                {
                    _averagePower.text = [NSString stringWithFormat:@"%f", _chan_lvls[channelIdx].mAveragePower];
                    _peakPower.text = [NSString stringWithFormat:@"%f", _chan_lvls[channelIdx].mPeakPower];
                }
            }
        }
    }
}

-(void)setChannelNumbers:(NSArray *)v
{
    [v retain];
    [_channelNumbers release];
    _channelNumbers = v;
}

- (IBAction)startstop: (id) sender
{
    if (mQueue) {
        [self stopMicrophone];
    } else {
        [self startMicrophone];
    }
}

@end

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ch_soft/article/details/7382666

智能推荐

tcp_tw_recycle 参数引发的tcp连接时而中断问题-程序员宅基地

文章浏览阅读499次。对该参数的理解简单来说,当开启了tcp_tw_recycle时,kernel会记录每个peer的最后一个报文的时戳,如果记录的该时戳仍然有效(距离当前时间小于TCP_PAWS_MSL),并且新收到的syn报文的时戳,比kernel记录的该peer的时戳还要小(换句话说,时光倒流了),那么就认为新收到的syn报文是有问题的(比如是某个在网络上兜兜转转了很久才到目的地址的syn),从而d..._关闭tcp_tw_recycle

删数问题_输出应包括所去掉的数字的位置和组成的新的正整数(n不超过100位)。要求:键盘-程序员宅基地

文章浏览阅读577次。问题描述:键盘输入一个高精度的正整数N,去掉其中任意kk个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和k,寻找一种方案使得剩下的数字组成的新数最小。输出应包括所去掉的数字的位置和组成的新的整数。(N不超过250位) 输入数据均不需判错。Simple Input1785434Simple Output13贪心策略:每次删掉的数都要保证删除数后剩下..._输出应包括所去掉的数字的位置和组成的新的正整数(n不超过100位)。要求:键盘

css--提示工具-程序员宅基地

文章浏览阅读72次。一、提示工具1、上提示工具代码如下 body{ margin:0; text-align:center; } .tooltip{ position:relative; display:inl..._color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px; at u

GRACE RL06版本的数据预处理_gramat处理rl06-程序员宅基地

文章浏览阅读5.2k次,点赞6次,收藏62次。function GRACE_RL06_preprocessing(controlfile_path)% Read the Control Filefid=fopen(controlfile_path,'r');num_file = fscanf(fid,'%d',1);radius_filter = fscanf(fid,'%d',1);destrip_method ..._gramat处理rl06

Ceph分布式存储实战:从0搭建一个存储集群,并把块设备镜像映射到CentOS 7系统上的步骤_映射到centos镜像中-程序员宅基地

文章浏览阅读1.2k次。本文配套视频和笔记:Ceph分布式存储实战(Ceph快速上手)B站视频在线观看链接:【千锋教育】Ceph分布式存储技术教程(进阶到实战)百度网盘链接:2020全新_Ceph分布式存储技术-进阶到实战 笔记+资料+视频.zip 提取码: xry9文章目录1. 分布式存储系统理论1.1. 认识分布式存储1.2. 分布式存储的特性1.2.1. 可扩展1.2.2. 低成本1.2.3. 高性能1.2.4. 易用1.2.5. 易管理1.3. 分布式存储的挑战和所涉及的技术1.4. 存储分类1.4.1. 本地_映射到centos镜像中

断言NSAssert()和NSParameterAssert区别和用处_nsassert 没有终止app-程序员宅基地

文章浏览阅读719次。NSAssert和assert是断言,主要的差别是assert在断言失败的时候只是简单的终止程序,而NSAssert会报告出错误信息并且打印出来.所以尽管的使用NSAssert,可以不去使用assert.iOS中用的最多的是两对断言, NSAssert/NSCAssert 和 NSParameterAssert/NSCparameterAssert. 要知道他们的区别,我们先来看看他们_nsassert 没有终止app

随便推点

Java反射机制<2>-程序员宅基地

文章浏览阅读44次。反射机制还可以调用类中的指定方法或指定属性,并且可以通过反射完成对数组的操作。通过反射调用类中的方法import java.lang.reflect.Method;//=================================================// File Name : InvokeChina_demo//-----------..._obj.getclass().getmethod传递的两个参数

软件维护_软件系统维护公告怎么写-程序员宅基地

文章浏览阅读1.5k次。软件维护软件维护主要是指根据需求变化或硬件环境的变化对应用程序进行部分或全部的修改,修改时应充分利用源程序。修改后要填写《程序修改登记表》,并在《程序变更通知书》上写明新旧程序的不同之处。目录1软件维护类型2改正性维护3适应性维护4完善性维护5预防性维护_软件系统维护公告怎么写

在xml文件中配置数据库url需要带多个参数的时候IDE提示The reference to entity "characterEncoding" must end with the ';'_xml treenode navigateurl 多个参数-程序员宅基地

文章浏览阅读1.7w次。原因xml文件中对”&”符需要转义解决办法把”&”改成“_&_” 以hibernate.cfg.xml文件的配置url来举例的话就是这样jdbc:mysql://localhost:3306/Test?useSSL=true&characterEncoding=utf8_xml treenode navigateurl 多个参数

eclipse java1.8,Eclipse 运行在JDK1.8.0之上,报错及解决方法-程序员宅基地

文章浏览阅读650次。Eclipse版本:Eclipse-jee-neon-3-win32-x86_64JDK版本:java version "1.8.0"Java(TM) SE Runtime Environment (build 1.8.0-b132)Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)启动报错:Java Virtual Mac..._eclipse version 1.8.0_38

微信文章阅读数点赞数查询API接口及实现(小数据量)_查询微信赞赏记录api-程序员宅基地

文章浏览阅读4.5w次。微信文章阅读数点赞数查询API接口及实现 研究过微信文章阅读和点赞数的同学都知道,如何获取这两个数字,关键在于获取微信有效的key.这个key的作用时间大概是2小时,而且也有访问频率的限制.访问频率可以用代码控制,大概10秒一次的速度就不会被屏蔽.接下来说如何完全自动获取有效的key. 可能大部分同学都会去搞手机上的key,一来我不知道怎么搞(Java开发不会Android囧_查询微信赞赏记录api

【必备算法】二分查找:LeetCode题(一)69. x 的平方根,367. 有效的完全平方数_请使用二分查找实现近似开根号函数 返回整数部分-程序员宅基地

文章浏览阅读1.4k次。在看二分查找的LeetCode题之前,先看看二分查找的基础知识:使用二分查找的条件:可以通过索引访问(链表不行)有序存在上下界二分查找的实质:二分查找不是一上来就去找指定值,而是不断进行定位缩小范围的过程主要用来对一些暴力迭代进行优化复杂度:O(logN)Java模板:int left = 0, right = arr.length - 1;while (left <= right) { // 这里是 <=,即arr[left=right]也要进入判断逻辑 _请使用二分查找实现近似开根号函数 返回整数部分

推荐文章

热门文章

相关标签