语音采集碰到一个问题,设定每次采集大小320个字节,采集到的数据偶尔会小于320个字节,导致把数据放入压缩库压缩时候会出现杂音问题。
pc android采集没发现这个情况,所以ios需要对语音数据对齐分切,经过测试能正常压缩语音数据,能清晰播放出来。
ios头文件
Recorder.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#define RECORDER_NOTIFICATION_CALLBACK_NAME @"recorderNotificationCallBackName"
#define kNumberAudioQueueBuffers 3 //缓冲区设定3个
#define kDefaultSampleRate 8000 //采样率
@interface Recorder : NSObject
{
AudioQueueRef _audioQueue;
AudioStreamBasicDescription audioFormat;
AudioQueueBufferRef _audioBuffers[kNumberAudioQueueBuffers];
char data[1024];
int offset;
}
@property (nonatomic, assign) BOOL isRecording;
@property (atomic, assign) NSUInteger sampleRate;
-(void)start;
-(void)stop;
@end
Recorder.m
#import "Recorder.h"
@implementation Recorder
- (id)init
{
self = [super init];
if (self) {
self.sampleRate = kDefaultSampleRate;
//设置录音 初始化录音参数
[self setupAudioFormat:kAudioFormatLinearPCM SampleRate:(int)self.sampleRate];
}
return self;
}
//设置录音 初始化录音参数
- (void)setupAudioFormat:(UInt32)inFormatID SampleRate:(int)sampeleRate
{
memset(&audioFormat, 0, sizeof(audioFormat));
audioFormat.mSampleRate = sampeleRate;//采样率
audioFormat.mChannelsPerFrame = 1;//单声道
audioFormat.mFormatID = inFormatID;//采集pcm 格式
audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
audioFormat.mBitsPerChannel = 16;//每个通道采集2个byte
audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = (audioFormat.mBitsPerChannel / 8) * audioFormat.mChannelsPerFrame;
audioFormat.mFramesPerPacket = 1;
}
//回调函数 不断采集声音。
void inputHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime,UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc)
{
Recorder *recorder = (__bridge Recorder*)inUserData;
int k = 0;
if (inBuffer->mAudioDataByteSize > 0) {
memcpy(recorder->data+recorder->offset,inBuffer->mAudioData,inBuffer->mAudioDataByteSize);//通过recorder->offset 偏移把语音数据存入recorder->data
recorder->offset+=inBuffer->mAudioDataByteSize;//记录语音数据的大小
k = recorder->offset/320;//计算语音数据有多个320个字节语音
for(int i = 0; i <k; i++)
{
NSData *SpeechData = [[NSData alloc]initWithBytes:recorder->data+i*320 length:320];//把recorder->data 数据以320个字节分切放入 传出的数组中。
[[NSNotificationCenter defaultCenter] postNotificationName:RECORDER_NOTIFICATION_CALLBACK_NAME object:SpeechData];
}
memcpy(recorder->data,recorder->data+k*320,recorder->offset-(k*320));//把剩下的语音数据放入原来的数组中
recorder->offset-=(k*320);//计算剩下的语音数据大小
}
if (recorder.isRecording) {
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}
}
//开始录音
-(void)start
{
NSError *error = nil;
//录音的设置和初始化
BOOL ret = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if (!ret) {
return;
}
//启用audio session
ret = [[AVAudioSession sharedInstance] setActive:YES error:&error];
if (!ret)
{
return;
}
//初始化缓冲语音数据数组
memset(data,0,1024);
offset = 0;
audioFormat.mSampleRate = self.sampleRate;
//初始化音频输入队列
AudioQueueNewInput(&audioFormat, inputHandler, (__bridge void *)(self), NULL, NULL, 0, &_audioQueue);
int bufferByteSize = 320; //设定采集一帧320个字节
//创建缓冲器
for (int i = 0; i < kNumberAudioQueueBuffers; ++i){
AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_audioBuffers[i]);
AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[i], 0, NULL);
}
//开始录音
AudioQueueStart(_audioQueue, NULL);
self.isRecording = YES;
}
//结束录音
-(void)stop
{
if (self.isRecording) {
self.isRecording = NO;
AudioQueueStop(_audioQueue, true);
AudioQueueDispose(_audioQueue, true);
[[AVAudioSession sharedInstance] setActive:NO error:nil];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:nil];
}
}
@end