录音
最近项目中需要录音功能,为此在写了一个录音的小模块。
首先需要添加AVFoundation.framework
lame.h 帮助链接
下面直接上代码
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
typedef void(^AudioModelBlock)(NSInteger duration);
@protocol AudioModelDelegate <NSObject>
- (void)recordResult:(NSString *)path error:(NSError *)error;
@end
@interface AudioModel : NSObject <AVAudioRecorderDelegate, AVAudioPlayerDelegate> {
AVAudioRecorder *_audioRecorder;
AVAudioPlayer *_audioPlayer;
NSTimer *_timer;
NSString *_name;
}
@property (nonatomic, assign) NSInteger duration;
@property (nonatomic, copy) AudioModelBlock block;
@property (nonatomic, assign) id <AudioModelDelegate> delegate;
#pragma mark - record
//开始录音 或 恢复录音
- (void)record;
//暂停录音
- (void)pauseRecotd;
//停止录音
- (void)stopRecord;
#pragma mark - play
//播放录音
- (void)playRecord:(NSString *)path;
#pragma mark - file
//移除录音
- (void)removeFile;
#import "AudioModel.h"
#import "lame.h"
@implementation AudioModel
- (instancetype)init {
self = [super init];
if (self) {
}
return self;
}
#pragma mark - setting
//获取录音文件设置
- (NSDictionary *)getAudioSetting {
NSMutableDictionary *dicM = [NSMutableDictionary dictionary];
//设置录音格式
[dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
//设置录音采样率
[dicM setObject:@(20000) forKey:AVSampleRateKey];
//设置通道,这里采用单声通道
[dicM setObject:@(2) forKey:AVNumberOfChannelsKey];
// //每个采样点位数,8,16,24,32
// [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
// [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
//音频编码质量
[dicM setObject:@(AVAudioQualityMedium) forKey:AVEncoderAudioQualityKey];
//其他
return dicM;
}
#pragma mark - path
- (NSString *)getBasePath {
NSString *urlStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
urlStr = [urlStr stringByAppendingPathComponent:[YLSaveObjectToLocal loadObjectForKey:@"userMid"]];
urlStr = [urlStr stringByAppendingPathComponent:[YLCoreTool getNowWithFormatter:@"yyyyMMdd"]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:urlStr]) {
}
else {
[fileManager createDirectoryAtPath:urlStr withIntermediateDirectories:YES attributes:nil error:nil];
}
return urlStr;
}
//获取录音文件保存路径
- (NSString *)getSavePath {
NSString *urlStr = [self getBasePath];
urlStr = [urlStr stringByAppendingPathComponent:_name];
urlStr = [urlStr stringByAppendingString:@".caf"];
return urlStr;
}
- (NSString *)getMp3FilePath {
NSString *urlStr = [self getBasePath];
urlStr = [urlStr stringByAppendingPathComponent:_name];
urlStr = [urlStr stringByAppendingString:@".mp3"];
return urlStr;
}
- (void)removeFile {
NSString *urlStr = [self getBasePath];
urlStr = [urlStr stringByAppendingPathComponent:_name];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *arr = @[@".caf",@".mp3"];
for (NSString *type in arr) {
NSString *path = [urlStr stringByAppendingString:type];
if ([fileManager fileExistsAtPath:path]) {
[fileManager removeItemAtPath:path error:nil];
}
}
}
#pragma mark - action
//开始录音 或 恢复录音
- (void)record {
_name = [YLCoreTool getNowWithFormatter:@"yyyyMMdd_HHmmss"];
//创建录音文件保存路径
NSURL *url = [NSURL URLWithString: [self getSavePath]];
//创建录音格式设置
NSDictionary *setting = [self getAudioSetting];
//设置音频会话 不然不录制
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
//设置为播放和录音状态,以便可以在录制完之后播放录音
[audioSession setCategory:AVAudioSessionCategoryRecord error:nil];
[audioSession setActive:YES error:nil];
//创建录音机
NSError *error = nil;
_audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:setting error:&error];
_audioRecorder.delegate = self;
if (error) {
NSLog(@"creat audioRecorder error:%@", error.localizedDescription);
} else {
if (![_audioRecorder isRecording]) {
if ([_audioRecorder prepareToRecord]) {
if ([_audioRecorder record]) {
self.timer.fireDate = [NSDate distantPast];
}
}
}
}
}
//暂停录音
- (void)pauseRecotd {
if ([_audioRecorder isRecording]) {
[_audioRecorder pause];
self.timer.fireDate = [NSDate distantFuture];
}
}
//停止录音
- (void)stopRecord {
[_audioRecorder stop];
self.timer.fireDate = [NSDate distantFuture];
}
//计时器计时
- (void)handleTimerAction {
self.duration = _audioRecorder.currentTime;
}
//播放录音
- (void)playRecord:(NSString *)path {
//播放声音小时可以设置
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory :AVAudioSessionCategoryPlayback error:nil];
[audioSession setActive:YES error:nil];
NSURL *url = [NSURL URLWithString: path];
NSError *error = nil;
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
_audioPlayer.numberOfLoops = 0;
_audioPlayer.delegate = self;
[_audioPlayer prepareToPlay];
if (error) {
NSLog(@"creat audioPlayer error: %@", error.localizedDescription);
}
else {
[_audioPlayer play];
}
}
- (void)playAVItem {
}
#pragma mark - AVAudioRecorderDelegate
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error {
if (error) {
NSLog(@"%@",error.localizedDescription);
}
}
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
NSLog(@"录制完成");
[self convertToMp3];
_audioRecorder = nil;
}
#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
_audioPlayer = nil;
}
#pragma mark - tool
- (void)convertToMp3
{
NSString *filePath = [self getMp3FilePath];
@try {
int read,write;
//只读方式打开被转换音频文件
FILE *pcm = fopen([[self getSavePath] cStringUsingEncoding:1], "rb");
fseek(pcm, 4 * 1024, SEEK_CUR);//删除头,否则在前一秒钟会有杂音
//只写方式打开生成的MP3文件
FILE *mp3 = fopen([filePath cStringUsingEncoding:1], "wb");
const int PCM_SIZE = 8192;
const int MP3_SIZE = 8192;
short int pcm_buffer[PCM_SIZE * 2];
unsigned char mp3_buffer[MP3_SIZE];
//这里要注意,lame的配置要跟AVAudioRecorder的配置一致,否则会造成转换不成功
lame_t lame = lame_init();
lame_set_VBR(lame, vbr_default);
lame_set_num_channels(lame,2);//默认为2双通道
lame_set_in_samplerate(lame, 20000);//11025.0
lame_set_brate(lame,8);
lame_set_mode(lame,3);
lame_set_quality(lame,5); /* 2=high 5 = medium 7=low 音质*/
lame_init_params(lame);
do {
//以二进制形式读取文件中的数据
read = (int)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, read, mp3_buffer, MP3_SIZE);
//二进制形式写数据到文件中 mp3_buffer:数据输出到文件的缓冲区首地址 write:一个数据块的字节数 1:指定一次输出数据块的个数 mp3:文件指针
fwrite(mp3_buffer, write, 1, mp3);
} while (read != 0);
lame_close(lame);
fclose(mp3);
fclose(pcm);
} @catch (NSException *exception) {
NSLog(@"%@",[exception description]);
} @finally {
NSLog(@"MP3生成成功!!!");
if ([self.delegate respondsToSelector:@selector(recordResult:error:)]) {
[self.delegate recordResult:[self getMp3FilePath] error:nil];
}
}
}
#pragma mark - set and get
- (void)setDuration:(NSInteger)duration {
_duration = duration;
if (self.block) {
self.block(_duration);
}
}
-(NSTimer *)timer{
if (!_timer) {
_timer=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(handleTimerAction) userInfo:nil repeats:YES];
}
return _timer;
}
/**
* 录音声波状态设置
*/
// _audioRecorder.meteringEnabled = YES;//如果需要监控声波则必须设置为YES
//-(void)audioPowerChange{
// [self.audioRecorder updateMeters];//更新测量值
// float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0
// CGFloat progress=(1.0/160.0)*(power+160.0);
// [self.audioPower setProgress:progress];
//}
// 获取音频时长
//AVURLAsset* audioAsset =[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:self.savePath] options:nil];
//CMTime audioDuration = audioAsset.duration;
//float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
音频和视频
音频和视频播放使用的ZFPlayer,有兴趣的话可以自己研究下
控制中心 后台播放
需要打开相应的Background Models
屏幕快照 2018-08-20 下午5.23.51.png
AppDelegate 中
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//处理中断事件的通知 播放
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterreption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
//开启后台处理多媒体事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
return YES;
}
//处理中断事件
-(void)handleInterreption:(NSNotification *)sender
{
ZFPlayerView *player = [ZFPlayerView sharedPlayerView];
if(player.state == ZFPlayerStatePause)
{
[player play];
}
else
{
[player pause];
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
//ZFPlayerView会监听UIApplicationWillResignActiveNotification,自动关闭后台播放
//这里是重新打开
[self handleInterreption:nil];
}
//控制中心操作
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
NSInteger order = -1;
switch (event.subtype) {
case UIEventSubtypeRemoteControlPause:
order = UIEventSubtypeRemoteControlPause;
break;
case UIEventSubtypeRemoteControlPlay:
order = UIEventSubtypeRemoteControlPlay;
break;
case UIEventSubtypeRemoteControlNextTrack:
order = UIEventSubtypeRemoteControlNextTrack;
break;
case UIEventSubtypeRemoteControlPreviousTrack:
order = UIEventSubtypeRemoteControlPreviousTrack;
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
order = UIEventSubtypeRemoteControlTogglePlayPause;
break;
default:
order = -1;
break;
}
NSDictionary *dict = @{@"order":@(order)};
[[NSNotificationCenter defaultCenter] postNotificationName:@"kAppDidReceiveRemoteControlNotification" object:nil userInfo:dict];
}
}
控制文件中,如ViewController等
这里需要添加MediaPlayer.framework
#import <MediaPlayer/MediaPlayer.h>
//监听控制中心事件
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listeningRemoteControl:) name:@"kAppDidReceiveRemoteControlNotification" object:nil];
}
//model 为自己定义
//播放时调用此函数更新控制中心
- (void)setNowPlayingInfo:(Model *)model {
NSMutableDictionary *songDict = [NSMutableDictionary dictionary];
//歌名
[songDict setObject:model.title forKey:MPMediaItemPropertyTitle];
//歌手名
[songDict setObject:AppName forKey:MPMediaItemPropertyArtist];
//总时长
[songDict setObject:[NSNumber numberWithInt:[model.time intValue] * 60] forKey:MPMediaItemPropertyPlaybackDuration];
//图片
MPMediaItemArtwork *imageItem = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"%@Logo", PREFIX_NAME]]];
[songDict setObject:imageItem forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songDict];
}
//控制中心事件操作
-(void)listeningRemoteControl:(NSNotification *)sender
{
NSDictionary *dict=sender.userInfo;
NSInteger order = [[dict objectForKey:@"order"] integerValue];
switch (order) {
//暂停
case UIEventSubtypeRemoteControlPause:
[self.playerView pause];
break;
//播放
case UIEventSubtypeRemoteControlPlay:
[self.playerView play];
break;
//暂停播放切换
case UIEventSubtypeRemoteControlTogglePlayPause:
{
if(self.playerView.state == ZFPlayerStatePause)
{
[self.playerView play];
}
else
{
[self.playerView pause];
}
break;
}
//下一首
case UIEventSubtypeRemoteControlNextTrack:
{
NSInteger index = [self.models indexOfObject:self.currentModel];
index = index + 1;
if (index < self.models.count) {
//这里是用于等待界面渲染
[NSTimer scheduledTimerWithTimeInterval:0.02 repeats:NO block:^(NSTimer * timer) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}];
}
break;
}
//上一首
case UIEventSubtypeRemoteControlPreviousTrack:
{
NSInteger index = [self.models indexOfObject:self.currentModel];
index = index - 1;
if (index >= 0) {
if (index < self.models.count) {
[NSTimer scheduledTimerWithTimeInterval:0.02 repeats:NO block:^(NSTimer * timer) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}];
}
}
break;
}
default:
break;
}
}
网友评论