iOS 录音 音频 视频 控制中心

2018-08-20  本文已影响101人  S大偉

录音

最近项目中需要录音功能,为此在写了一个录音的小模块。

首先需要添加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;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读