本文需要引入lame.h,以及第三方资源EMVoiceConverter
定义协议规则
@protocol PlayAVAudioManagerDelegate
- (void)playerBeiginLoadVoice;
- (void)playerBeiginPlay;
- (void)playerDidFinishPlay;
@end
@protocol RecordAVAudioDelegate
- (void)recordFail;
- (void)recordBeginConvert;
- (void)recordFinishedWithFilePath:(NSString *)filePath duration:(double)duration;
- (void)recordVoiceWithAvergePower:(float)average peakPower:(float)peak;
@end
自定义对象
@interface AVAudioManager : NSObject
@property (nonatomic, assign)id<PlayAVAudioManagerDelegate> playDelegate;
@property (nonatomic, assign)id<RecordAVAudioDelegate> recordDelegate;
+ (AVAudioManager *)sharedInstance;
//play
- (void)playVoiceWithUrl:(NSURL *)voiceUrl;
- (void)playVoiceWithData:(NSData *)voiceData;
- (void)stopPlay;
//record
- (void)startRecord;
- (void)stopRecord;
- (void)cancelRecord;
@end
static const int kCallBackInterval = 1;
@interface AVAudioManager ()
@property (nonatomic, strong) AVAudioSession *session;
@property (nonatomic, strong) AVAudioRecorder *recorder;
@property (nonatomic ,strong) AVAudioPlayer *player;
@property (nonatomic, strong) NSString *path;
@property (nonatomic, strong, readonly) NSString *cafPath;
@property (nonatomic, strong) NSString *mp3Path;
@property (nonatomic, strong) NSTimer *recordCallBackTimer;
@end
实现如下
@implementation AVAudioManager
+ (AVAudioManager *)sharedInstance {
static AVAudioManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (NSTimer *)recordCallBackTimer {
if(_recordCallBackTimer == nil) {
_recordCallBackTimer = [NSTimer scheduledTimerWithTimeInterval:kCallBackInterval target:self selector:@selector(recordCallBackAction) userInfo:nil repeats:YES];
}
return _recordCallBackTimer;
}
- (void)recordCallBackAction {
if(!self.recorder.isRecording) {
return;
}
[self.recorder updateMeters];
if([self.recordDelegate respondsToSelector:@selector(recordVoiceWithAvergePower:peakPower:)]) {
float peak = [self.recorder peakPowerForChannel:0],
averge = [self.recorder averagePowerForChannel:0];
[self.recordDelegate recordVoiceWithAvergePower:averge peakPower:peak];
}
}
- (AVAudioRecorder *)recorder {
if(_recorder == nil) {
NSError *recorderSetupError = nil;
NSURL *url = [NSURL fileURLWithPath:self.cafPath];
NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];
//录音格式 无法使用
[settings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
//采样率,注意这里的采样率不要随意调整,太高会导致录音失真。
[settings setValue :[NSNumber numberWithFloat:44100.0f] forKey: AVSampleRateKey];//44100.0
//通道数
[settings setValue :[NSNumber numberWithInt:2] forKey: AVNumberOfChannelsKey];
//音频质量,采样质量
[settings setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
_recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&recorderSetupError];
if(recorderSetupError) {
NSLog(@"%@",recorderSetupError);
}
_recorder.delegate = self;
}
return _recorder;
}
- (AVAudioSession *)session {
if(_session == nil) {
_session = [AVAudioSession sharedInstance];
NSError *sessionError;
[_session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
[_session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
[_session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
if(_session == nil) {
NSLog(@"Error creating session: %@", [sessionError description]);
}
}
return _session;
}
- (NSString *)cafPath {
return [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.caf"];
}
- (NSString *)mp3Path {
return _mp3Path ? : (_mp3Path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp3",@([NSDate timeIntervalSinceReferenceDate])]]);
}
- (void)playVoiceWithUrl:(NSURL *)voiceUrl {
dispatch_async(dispatch_queue_create("playSoundFromUrl", NULL),^{
[self.playDelegate playerBeiginLoadVoice];
NSData *data = nil;
if(voiceUrl.isFileURL) {
//播放的时候需要转化下格式。
if(![EMVoiceConverter isAMRFile:voiceUrl.absoluteString]) {
data = [NSData dataWithContentsOfURL:voiceUrl];
}
else {
NSString *wavFilePath = [[voiceUrl.absoluteString stringByDeletingPathExtension] stringByAppendingPathExtension:@"wav"];
if(![[NSFileManager defaultManager] fileExistsAtPath:voiceUrl.absoluteString]) {
[EMVoiceConverter amrToWav:voiceUrl.absoluteString wavSavePath:wavFilePath];
data = [NSData dataWithContentsOfFile:wavFilePath];
}
}
}
else {
data = [NSData dataWithContentsOfURL:voiceUrl];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self playVoiceWithData:data];
});
});
}
//播放逻辑
- (void)playVoiceWithData:(NSData *)voiceData {
[self setupPlaySound];
if (_player) {
[_player stop];
_player.delegate = nil;
_player = nil;
}
NSError *playerError;
_player = [[AVAudioPlayer alloc] initWithData:voiceData error:&playerError];
_player.volume = 1.0f;
if(_player == nil) {
NSLog(@"ERror creating player: %@", [playerError description]);
return;
}
_player.delegate = self;
[_player play];
[self.playDelegate playerBeiginPlay];
}
- (void)stopPlay {
if (self.player.isPlaying) {
[self.player stop];
}
}
-(void)setupPlaySound {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
[self.playDelegate playerDidFinishPlay];
}
- (void)applicationWillResignActive:(UIApplication *)application {
[self.playDelegate playerDidFinishPlay];
}
//录音逻辑
- (void)startRecord {
self.mp3Path = nil;
self.session = nil;
// 判断权限问题
if ([self.session respondsToSelector:@selector(requestRecordPermission:)]) {
[self.session performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
if (!granted) {
NSLog(@"disable");
return;
}
}];
}
[self.session setActive:YES error:nil];
self.recorder.meteringEnabled = YES;
[self.recorder prepareToRecord];
[self.recorder record];
[self.recordCallBackTimer fire];
}
- (void)stopRecord {
double cTime = self.recorder.currentTime;
[self.recorder stop];
[self closeTimerRecord];
if (cTime > 1) {
[self audio_PCMtoMP3WithDuration:cTime];
}else {
[_recorder deleteRecording];
if ([self.recordDelegate respondsToSelector:@selector(recordFail)]) {
[self.recordDelegate recordFail];
}
}
}
- (void)cancelRecord {
[self.recorder stop];
[self.recorder deleteRecording];
[self closeTimerRecord];
}
- (void)closeTimerRecord {
[self.recordCallBackTimer invalidate];
self.recordCallBackTimer = nil;
}
#pragma mark - Convert Utils
//格式转换,兼容Android
- (void)audio_PCMtoMP3WithDuration:(double)timer {
NSString *cafFilePath = self.cafPath;
NSString *mp3FilePath = self.mp3Path;
// remove the old mp3 file
[self deleteMp3Cache];
NSLog(@"MP3转换开始");
if ([self.recordDelegate respondsToSelector:@selector(recordBeginConvert)]) {
[self.recordDelegate recordBeginConvert];
}
@try {
size_t read;
int write;
FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb"); //source 被转换的音频文件位置
fseek(pcm, 4*1024, SEEK_CUR); //skip file header
FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb"); //output 输出生成的Mp3文件位置
const int PCM_SIZE = 8192;
const int MP3_SIZE = 8192;
short int pcm_buffer[PCM_SIZE*2];
unsigned char mp3_buffer[MP3_SIZE];
lame_t lame = lame_init();
lame_set_in_samplerate(lame, 11025.0);
lame_set_VBR(lame, vbr_default);
lame_init_params(lame);
do {
read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
if (read == 0) {
write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
}
else {
write = lame_encode_buffer_interleaved(lame, pcm_buffer, (int)read, mp3_buffer, MP3_SIZE);
}
fwrite(mp3_buffer, write, 1, mp3);
} while (read != 0);
lame_close(lame);
fclose(mp3);
fclose(pcm);
}
@catch (NSException *exception) {
NSLog(@"%@",[exception description]);
}
@finally {
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategorySoloAmbient error: nil];
}
[self deleteCafCache];
NSLog(@"MP3转换结束");
if ([self.recordDelegate respondsToSelector:@selector(recordFinishedWithFilePath:duration:)]) {
[self.recordDelegate recordFinishedWithFilePath:self.mp3Path duration:timer];
}
}
- (void)deleteMp3Cache {
[self deleteFileWithPath:self.mp3Path];
}
- (void)deleteCafCache {
[self deleteFileWithPath:self.cafPath];
}
- (void)deleteFileWithPath:(NSString *)path {
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:path error:nil];
}
网友评论