淘宝网声音采集器怎么能让声音接收不到呢 声音采集器

发布时间:

一、产品介绍

1、ISRAE1防爆声波(声发射)采集器包括传感器、采集器、连接传感器和采集器的同轴电缆

2、ISRAE1防爆声波(声发射)采集器是严格按照《国家GB3836系列标准》而设计的,不会产生电火花点燃易燃易爆气体,可整体放置在1区、2区使用。适用于防爆要求的封闭环境中的设备检测等。

3、传感器内置34dB前放,可以使得连接传感器和采集器的同轴电缆长度达到100米。采集器主体是一个铝合金外壳小圆筒,内置滤波,AD,处理器,存储器,4G传输模块等,底部带磁性可磁吸固定。

3、登录云端,可设置合理的门限、采样率、HDT、HLT、EET等参数,根据现场噪声云端设置最终采样参数。

4、多种采集方式,用户可根据需求在云端选择连续采集、定时采集、间隔采集等采集方式。

5、可从云端实时查看数据及下载原始数据,并通过SWAE深入分析软件分析数据。

二、防爆基本参数

型号:ISRAE1

防爆标志:Ex mb IIC T4 Gb

额定电压:12VDC

额定电流:300mA

使用区域:1区、2区

允许的预期短路电流:1A

符合标准:GB3836系列

环境使用温度:-20℃~+60℃

产品尺寸:采集器尺寸:Φ80mm×90mm

重量:约1kg

传感器尺寸:Φ30mm×37mm

防护等级:IP67

采集器外壳图纸示意图

三、性能特点

1、可整体放置在防爆区使用。

2、传感器内置前放,传感器与采集器线之间的同轴电缆长度可达100米。

3、大容量64G SD卡实时存储波形数据。

4、云端可对采集器实时设置采样参数,如门限、采样率、采集方式及其他。

5、数据可通过4G的方式上传至云端服务器。

6、云端可实时浏览参数、波形、报警数据。也可浏览历史参数、波形、评级数据。

7、原始数据是标准Soundwel波形数据格式及功能,可用SWAE软件进行深度分析。

防爆采集器组网示意图

四、技术参数

采集器技术参数

采样频率:2MHz/16bit

采样长度:可调

噪声/动态范围:35dB/65dB(0dB=1uv)

信号频率:3KHz~500KHz

输入范围:±100mv(100dB)

数据通讯:4G

防护等级:IP67

存储容量:64G SD 卡

环境温度:-20℃~﹢60℃

尺寸:Φ80mm×90mm

传感器技术参数

谐振频率:150kHz

频率范围:60kHz-400kHz

灵敏度峰值:>75dB

尺寸:Φ30mm×37mm

重量:约110g

防护等级:IP67

五、采集器和传感器安装要求

采集器安装:

1、需对采集器做遮光保护;

2、对该防爆声发射采集器供电应符合当地的防爆要求,如对电源增加安全栅、穿管走线要求等。

传感器安装:

传感器接收面涂有声发射专用耦合剂,与试样之间保持良好的声耦合效果,使用磁性夹具或其他辅助固定工具对传感器进行固定。或使用固化耦合剂永久固定传感器。

六、防爆证书



需求

iOS中使用Audio unit实现音频数据采集,直接采集PCM无损数据, Audio Unit不能直接采集压缩数据,在以后的文章会讲到音频压缩.

实现原理

使用Audio Unit采集硬件输入端,如麦克风,其他外置具备麦克风功能设备(带麦的耳机,话筒等,前提是其本身要和苹果兼容).

阅读前提

Core Audio基本原理:简书,掘金,博客

Audio Unit概念篇:简书,掘金,博客

Audio Session基础:简书,掘金,博客

音视频基础知识

C,C++基本知识

本文直接为实战篇,如需了解理论基础参考上述链接中的内容,本文侧重于实战中注意点.

本项目实现低耦合,高内聚,所以直接将相关模块拖入你的项目设置参数就可直接使用.

GitHub地址(附代码) : Audio Unit Capture

简书地址 : Audio Unit Capture

掘金地址 : Audio Unit Capture

博客地址 : Audio Unit Capture

1、具体实现1.1 代码结构

如上所示,我们总体分为两大类,一个是负责采集的类,一个是负责做音频录制的类,你可以根据需求在适当时机启动,关闭Audio Unit, 并且在Audio Unit已经启动的情况下可以进行音频文件录制,前面需求仅仅需要如下四个API即可完成.

// Start / Stop Audio Queue [[XDXAudioCaptureManager getInstance] startAudioCapture]; [[XDXAudioCaptureManager getInstance] stopAudioCapture]; // Start / Stop Audio Record [[XDXAudioQueueCaptureManager getInstance] startRecordFile]; [[XDXAudioQueueCaptureManager getInstance] stopRecordFile];1.2 初始化audio unit

本例采用单例实现,故将audio unit的实现放在初始化中,仅执行一次,如果销毁了audio unit则需要在外层重新调用初始化API,一般不建议反复销毁创建audio unit,所以最好就是在单例初始化中配置audio unit其后仅仅需要打开关闭即可.

iPhone设备默认仅支持单声道,如果设置双声道代码无法正常初始化. 如果需要模拟双声道,可以手动用代码对单声道数据做一次拷贝.具体方法以后文章会讲到.

注意: 这里的采样buffer大小的设置与采样时间的设置不可随意设置,换句话说,当采样时间一定,我们设置的采样数据大小不能超过其最大值,可通过公式算出采样时间与采样数据的关系.

采样公式计算

数据量(字节 / 秒)=(采样频率(Hz)* 采样位数(bit)* 声道数)/ 8 - (instancetype)init { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [super init]; // Note: audioBufferSize can not more than durationSec max size. [_instace configureAudioInfoWithDataFormat:&m_audioDataFormat formatID:kAudioFormatLinearPCM sampleRate:44100 channelCount:1 audioBufferSize:2048 durationSec:0.02 callBack:AudioCaptureCallback]; }); return _instace; - (void)configureAudioInfoWithDataFormat:(AudioStreamBasicDescription *)dataFormat formatID:(UInt32)formatID sampleRate:(Float64)sampleRate channelCount:(UInt32)channelCount audioBufferSize:(int)audioBufferSize durationSec:(float)durationSec callBack:(AURenderCallback)callBack { // Configure ASBD [self configureAudioToAudioFormat:dataFormat byParamFormatID:formatID sampleRate:sampleRate channelCount:channelCount]; // Set sample time [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:durationSec error:NULL]; // Configure Audio Unit m_audioUnit = [self configreAudioUnitWithDataFormat:*dataFormat audioBufferSize:audioBufferSize callBack:callBack]; } }1.3 设置音频流数据格式 ASBD

注意点

需要注意的是,音频数据格式与硬件直接相关,如果想获取最高性能,最好直接使用硬件本身的采样率,声道数等音频属性,所以,如采样率,当我们手动进行更改后,Audio Unit会在内部自行转换一次,虽然代码上没有感知,但一定程序上还是降低了性能.

iOS中不支持直接设置双声道,如果想模拟双声道,可以自行填充音频数据,具体会在以后的文章中讲到,喜欢请持续关注.

获取音频属性值

理解AudioSessionGetProperty函数,该函数表明查询当前硬件指定属性的值,如下,
kAudioSessionProperty_CurrentHardwareSampleRate为查询当前硬件采样率,
kAudioSessionProperty_CurrentHardwareInputNumberChannels为查询当前采集的声道数.因为本例中使用手动赋值方式更加灵活,所以没有使用查询到的值.

设置不同格式定制的属性

首先,你必须了解未压缩格式(PCM...)与压缩格式(AAC...). 使用iOS直接采集未压缩数据是可以直接拿到硬件采集到的数据,由于audio unit不能直接采集aac类型数据,所以这里仅采集原始的PCM数据.

使用PCM数据格式必须设置采样值的flag:mFormatFlags,每个声道中采样的值换算成二进制的位宽mBitsPerChannel,iOS中每个声道使用16位的位宽,每个包中有多少帧mFramesPerPacket,对于PCM数据而言,因为其未压缩,所以每个包中仅有1帧数据.每个包中有多少字节数(即每一帧中有多少字节数),可以根据如下简单计算得出

注意,如果是其他压缩数据格式,大多数不需要单独设置以上参数,默认为0.这是因为对于压缩数据而言,每个音频采样包中压缩的帧数以及每个音频采样包压缩出来的字节数可能是不同的,所以我们无法预知进行设置,就像mFramesPerPacket参数,因为压缩出来每个包具体有多少帧只有压缩完成后才能得知.

#define kXDXAudioPCMFramesPerPacket 1 #define KXDXAudioBitsPerChannel 16 -(void)configureAudioToAudioFormat:(AudioStreamBasicDescription *)audioFormat byParamFormatID:(UInt32)formatID sampleRate:(Float64)sampleRate channelCount:(UInt32)channelCount { AudioStreamBasicDescription dataFormat = {0}; UInt32 size = sizeof(dataFormat.mSampleRate); // Get hardware origin sample rate. (Recommended it) Float64 hardwareSampleRate = 0; AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &hardwareSampleRate); // Manual set sample rate dataFormat.mSampleRate = sampleRate; size = sizeof(dataFormat.mChannelsPerFrame); // Get hardware origin channels number. (Must refer to it) UInt32 hardwareNumberChannels = 0; AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, &size, &hardwareNumberChannels); dataFormat.mChannelsPerFrame = channelCount; dataFormat.mFormatID = formatID; if (formatID == kAudioFormatLinearPCM) { dataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; dataFormat.mBitsPerChannel = KXDXAudioBitsPerChannel; dataFormat.mBytesPerPacket = dataFormat.mBytesPerFrame = (dataFormat.mBitsPerChannel / 8) * dataFormat.mChannelsPerFrame; dataFormat.mFramesPerPacket = kXDXAudioPCMFramesPerPacket; } memcpy(audioFormat, &dataFormat, sizeof(dataFormat)); NSLog(@"%@: %s - sample rate:%f, channel count:%d",kModuleName, __func__,sampleRate,channelCount); }1.4 设置采样时间

使用AVAudioSession可以设置采样时间,注意,在采样时间一定的情况下,我们设置的采样大小不能超过其最大值.

数据量(字节 / 秒)=(采样频率(Hz)* 采样位数(bit)* 声道数)/ 8

比如: 采样率是44.1kHz, 采样位数是16, 声道数是1, 采样时间为0.01秒,则最大的采样数据为882. 所以即使我们设置超过此数值,系统最大也只能采集882个字节的音频数据.

[[AVAudioSession sharedInstance] setPreferredIOBufferDuration:durationSec error:NULL];

C++音视频学习资料免费获取方法:关注音视频开发T哥,点击即可免费获取2023年最新C++音视频开发进阶独家免费学习大礼包!

1.5 配置Audio Unit

m_audioUnit = [self configreAudioUnitWithDataFormat:*dataFormat audioBufferSize:audioBufferSize callBack:callBack]; - (AudioUnit)configreAudioUnitWithDataFormat:(AudioStreamBasicDescription)dataFormat audioBufferSize:(int)audioBufferSize callBack:(AURenderCallback)callBack { AudioUnit audioUnit = [self createAudioUnitObject]; if (!audioUnit) { return NULL; } [self initCaptureAudioBufferWithAudioUnit:audioUnit channelCount:dataFormat.mChannelsPerFrame dataByteSize:audioBufferSize]; [self setAudioUnitPropertyWithAudioUnit:audioUnit dataFormat:dataFormat]; [self initCaptureCallbackWithAudioUnit:audioUnit callBack:callBack]; // Calls to AudioUnitInitialize() can fail if called back-to-back on different ADM instances. A fall-back solution is to allow multiple sequential calls with as small delay between each. This factor sets the max number of allowed initialization attempts. OSStatus status = AudioUnitInitialize(audioUnit); if (status != noErr) { NSLog(@"%@: %s - couldn't init audio unit instance, status : %d \n",kModuleName,__func__,status); } return audioUnit; }

创建audio unit对象

这里可以指定使用audio unit哪个分类创建. 这里使用的
kAudioUnitSubType_VoiceProcessingIO分类是做回声消除及增强人声的分类,如果仅仅需要原始未处理音频数据也可以改用
kAudioUnitSubType_RemoteIO分类,如果想了解更多关于audio unit分类,文章最上方有相关链接可以访问.

AudioComponentFindNext:第一个参数设置为NULL表示使用系统定义的顺序查找第一个匹配的audio unit.如果你将上一个使用的audio unit引用传给该参数,则该函数将继续寻找下一个与之描述匹配的audio unit.

- (AudioUnit)createAudioUnitObject { AudioUnit audioUnit; AudioComponentDescription audioDesc; audioDesc.componentType = kAudioUnitType_Output; audioDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;//kAudioUnitSubType_RemoteIO; audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple; audioDesc.componentFlags = 0; audioDesc.componentFlagsMask = 0; AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc); OSStatus status = AudioComponentInstanceNew(inputComponent, &audioUnit); if (status != noErr) { NSLog(@"%@: %s - create audio unit failed, status : %d \n",kModuleName, __func__, status); return NULL; }else { return audioUnit; } }

创建一个接收采集到音频数据的数据结构


kAudioUnitProperty_ShouldAllocateBuffer: 默认为true, 它将创建一个回调函数中接收数据的buffer, 在这里设置为false, 我们自己定义了一个bufferList用来接收采集到的音频数据.

- (void)initCaptureAudioBufferWithAudioUnit:(AudioUnit)audioUnit channelCount:(int)channelCount dataByteSize:(int)dataByteSize { // Disable AU buffer allocation for the recorder, we allocate our own. UInt32 flag = 0; OSStatus status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Output, INPUT_BUS, &flag, sizeof(flag)); if (status != noErr) { NSLog(@"%@: %s - could not allocate buffer of callback, status : %d \n", kModuleName, __func__, status); } AudioBufferList * buffList = (AudioBufferList*)malloc(sizeof(AudioBufferList)); buffList->mNumberBuffers = 1; buffList->mBuffers[0].mNumberChannels = channelCount; buffList->mBuffers[0].mDataByteSize = dataByteSize; buffList->mBuffers[0].mData = (UInt32 *)malloc(dataByteSize); m_buffList = buffList; }

设置audio unit属性

kAudioUnitProperty_StreamFormat: 通过先前创建的ASBD设置音频数据流的格式

kAudioOutputUnitProperty_EnableIO: 启用/禁用 对于 输入端/输出端

input bus / input element: 连接设备硬件输入端(如:麦克风)

output bus / output element: 连接设备硬件输出端(如:扬声器)

input scope: 每个element/scope可能有一个input scope或output scope,以采集为例,音频从audio unit的input scope流入,我们仅仅只能从output scope中获取音频数据.因为input scope是audio unit与硬件之间的交互.所以你可以看到代码中设置的两项INPUT_BUS,kAudioUnitScope_Output.

remote I/O audio unit默认是打开输出端,关闭输入端的,而本文讲的是利用audio unit做音频数据采集,所以我们要打开输入端,禁止输出端.

- (void)setAudioUnitPropertyWithAudioUnit:(AudioUnit)audioUnit dataFormat:(AudioStreamBasicDescription)dataFormat { OSStatus status; status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, INPUT_BUS, &dataFormat, sizeof(dataFormat)); if (status != noErr) { NSLog(@"%@: %s - set audio unit stream format failed, status : %d \n",kModuleName, __func__,status); } /* // remove echo but can not effect by testing. UInt32 echoCancellation = 0; AudioUnitSetProperty(m_audioUnit, kAUVoiceIOProperty_BypassVoiceProcessing, kAudioUnitScope_Global, 0, &echoCancellation, sizeof(echoCancellation)); */ UInt32 enableFlag = 1; status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, INPUT_BUS, &enableFlag, sizeof(enableFlag)); if (status != noErr) { NSLog(@"%@: %s - could not enable input on AURemoteIO, status : %d \n",kModuleName, __func__, status); } UInt32 disableFlag = 0; status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, OUTPUT_BUS, &disableFlag, sizeof(disableFlag)); if (status != noErr) { NSLog(@"%@: %s - could not enable output on AURemoteIO, status : %d \n",kModuleName, __func__,status); } }

注册回调函数接收音频数据

- (void)initCaptureCallbackWithAudioUnit:(AudioUnit)audioUnit callBack:(AURenderCallback)callBack { AURenderCallbackStruct captureCallback; captureCallback.inputProc = callBack; captureCallback.inputProcRefCon = (__bridge void *)self; OSStatus status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, INPUT_BUS, &captureCallback, sizeof(captureCallback)); if (status != noErr) { NSLog(@"%@: %s - Audio Unit set capture callback failed, status : %d \n",kModuleName, __func__,status); } }1.6 开启audio unit

直接调用AudioOutputUnitStart即可开启audio unit.如果以上配置都正确,audio unit可以直接工作.

- (void)startAudioCaptureWithAudioUnit:(AudioUnit)audioUnit isRunning:(BOOL *)isRunning { OSStatus status; if (*isRunning) { NSLog(@"%@: %s - start recorder repeat \n",kModuleName,__func__); return; } status = AudioOutputUnitStart(audioUnit); if (status == noErr) { *isRunning = YES; NSLog(@"%@: %s - start audio unit success \n",kModuleName,__func__); }else { *isRunning = NO; NSLog(@"%@: %s - start audio unit failed \n",kModuleName,__func__); } } 复制代码1.7 回调函数中处理音频数据

inRefCon:开发者自己定义的任何数据,一般将本类的实例传入,因为回调函数中无法直接调用OC的属性与方法,此参数可以作为OC与回调函数沟通的桥梁.即传入本类对象.

ioActionFlags: 描述上下文信息

inTimeStamp: 包含采样的时间戳

inBusNumber: 调用此回调函数的总线数量

inNumberFrames: 此次调用包含了多少帧数据

ioData: 音频数据.

AudioUnitRender: 使用此函数将采集到的音频数据赋值给我们定义的全局变量m_buffList

static OSStatus AudioCaptureCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { AudioUnitRender(m_audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, m_buffList); XDXAudioCaptureManager *manager = (__bridge XDXAudioCaptureManager *)inRefCon; /* Test audio fps static Float64 lastTime = 0; Float64 currentTime = CMTimeGetSeconds(CMClockMakeHostTimeFromSystemUnits(inTimeStamp->mHostTime))*1000; NSLog(@"Test duration - %f",currentTime - lastTime); lastTime = currentTime; */ void *bufferData = m_buffList->mBuffers[0].mData; UInt32 bufferSize = m_buffList->mBuffers[0].mDataByteSize; // NSLog(@"demon = %d",bufferSize); if (manager.isRecordVoice) { [[XDXAudioFileHandler getInstance] writeFileWithInNumBytes:bufferSize ioNumPackets:inNumberFrames inBuffer:bufferData inPacketDesc:NULL]; } return noErr; }1.8 停止audio unit

AudioOutputUnitStop : 停止audio unit.

-(void)stopAudioCaptureWithAudioUnit:(AudioUnit)audioUnit isRunning:(BOOL *)isRunning { if (*isRunning == NO) { NSLog(@"%@: %s - stop capture repeat \n",kModuleName,__func__); return; } *isRunning = NO; if (audioUnit != NULL) { OSStatus status = AudioOutputUnitStop(audioUnit); if (status != noErr){ NSLog(@"%@: %s - stop audio unit failed. \n",kModuleName,__func__); }else { NSLog(@"%@: %s - stop audio unit successful",kModuleName,__func__); } } }1.9 释放audio unit

当我们彻底不使用audio unit时,可以释放本类audio unit相关的资源,注意释放具有先后顺序,首先应停止audio unit, 然后将初始化状态还原,最后释放audio unit所有相关内存资源.

- (void)freeAudioUnit:(AudioUnit)audioUnit { if (!audioUnit) { NSLog(@"%@: %s - repeat call!",kModuleName,__func__); return; } OSStatus result = AudioOutputUnitStop(audioUnit); if (result != noErr){ NSLog(@"%@: %s - stop audio unit failed.",kModuleName,__func__); } result = AudioUnitUninitialize(m_audioUnit); if (result != noErr) { NSLog(@"%@: %s - uninitialize audio unit failed, status : %d",kModuleName,__func__,result); } // It will trigger audio route change repeatedly result = AudioComponentInstanceDispose(m_audioUnit); if (result != noErr) { NSLog(@"%@: %s - dispose audio unit failed. status : %d",kModuleName,__func__,result); }else { audioUnit = nil; } }1.10 音频文件录制

此部分可参考另一篇文章: 音频文件录制

简书地址 : Audio File Record

掘金地址 : Audio File Record

博客地址 : Audio File Record

原文链接: