zoukankan      html  css  js  c++  java
  • IOS开发:录音功能,播放功能的完整实现demo

    我是在别人的demo基础上继续做的,百度cui ios ,博客园的那个,那人博客写的相当仔细,大家想学的更多可以去他博客看看

    原demo只有录音界面,然后录音结束自动播放的功能。

    录音界面我稍微改了下,本身是录音结束就自动播放,我是选择了在别的界面播放。然后是取路径命名的问题,我改为了用当前时间命名。原demo是用myRecord.caf作为结尾命名,因为原demo录完就播放嘛。

    录音界面有个列表按钮,跳到第二个界面,可看到具体的录音栏目。然后点击右侧的按钮,进入第三个页面自动播放,然后是暂停继续等等

    第三个页面自动播放,其实就是把创建音频播放器对象写在了load里,然后调用下音频会话,跟第一个界面的东西基本一样的思路。

    然后还有一个关键点是第二个界面点按钮跳转的时候,你必须知道你点的是第几行,也就是你点的是哪个录音,把这个路径送到第三个界面给音频对象用,这个采用单例模式实现跨界面传值。

    还有就是页面间的跳转,一种直接拉线,一种是用代码,我的随笔里也写了用代码的。

     

    界面设计

    录音界面代码

      1 #import "ViewController.h"
      2 #import <AVFoundation/AVFoundation.h>
      3 
      4 @interface ViewController ()<AVAudioRecorderDelegate>
      5 
      6 @property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音频录音机
      7 @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音频播放器,用于播放录音文件
      8 @property (nonatomic,strong) NSTimer *timer;//录音声波监控(注意这里暂时不对播放进行监控)
      9 
     10 @property (weak, nonatomic) IBOutlet UIButton *record;//开始录音
     11 @property (weak, nonatomic) IBOutlet UIButton *pause;//暂停录音
     12 @property (weak, nonatomic) IBOutlet UIButton *resume;//恢复录音
     13 @property (weak, nonatomic) IBOutlet UIButton *stop;//停止录音
     14 @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音频波动
     15 @property (weak, nonatomic) IBOutlet UIButton *exit;//退出
     16 
     17 @end
     18 
     19 @implementation ViewController
     20 
     21 #pragma mark - 控制器视图方法
     22 - (void)viewDidLoad {
     23     //f改变progressview的长宽
     24     _audioPower.transform=CGAffineTransformMakeScale(1.0f,4.0f);
     25     [super viewDidLoad];
     26     //自动播放的
     27     [self setAudioSession];
     28 }
     29 
     30 #pragma mark - 私有方法
     31 
     32 
     33 
     34 /**
     35  *  取得录音文件保存路径
     36  *
     37  *  @return 录音文件路径
     38  */
     39 -(NSURL *)getSavePath{
     40     //获取文件路径
     41     NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
     42     NSLog(@"%@",urlStr);
     43     NSDate *date=[NSDate date];
     44     NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
     45     [dateFormatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
     46     //NSDate转NSString
     47     NSString *sss = [dateFormatter stringFromDate:date];
     48     NSString *ccc=[sss stringByAppendingString:@".caf"];
     49     urlStr=[urlStr stringByAppendingPathComponent:ccc];
     50     NSURL *url=[NSURL fileURLWithPath:urlStr];
     51    // NSLog(@"第一个页面的路径",url);
     52     return url;
     53 }
     54 
     55 /**
     56  *  取得录音文件设置
     57  *
     58  *  @return 录音设置
     59  */
     60 -(NSDictionary *)getAudioSetting{
     61     NSMutableDictionary *dicM=[NSMutableDictionary dictionary];
     62     //设置录音格式
     63     [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
     64     //设置录音采样率,8000是电话采样率,对于一般录音已经够了
     65     [dicM setObject:@(8000) forKey:AVSampleRateKey];
     66     //设置通道,这里采用单声道
     67     [dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
     68     //每个采样点位数,分为8、16、24、32
     69     [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
     70     //是否使用浮点数采样
     71     [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
     72     //....其他设置等
     73     return dicM;
     74 }
     75 
     76 /**
     77  *  获得录音机对象
     78  *
     79  *  @return 录音机对象
     80  */
     81 -(AVAudioRecorder *)audioRecorder{
     82     if (!_audioRecorder) {
     83         //创建录音文件保存路径
     84         NSURL *url=[self getSavePath];
     85         //创建录音格式设置
     86         NSDictionary *setting=[self getAudioSetting];
     87         //创建录音机
     88         NSError *error=nil;
     89         _audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
     90         _audioRecorder.delegate=self;
     91         _audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES
     92         if (error) {
     93             NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
     94             return nil;
     95         }
     96     }
     97     return _audioRecorder;
     98 }
     99 -(void)setAudioSession{
    100     AVAudioSession *audioSession=[AVAudioSession sharedInstance];
    101     //设置为录音状态
    102     [audioSession setCategory:AVAudioSessionCategoryRecord error:nil];
    103     [audioSession setActive:YES error:nil];
    104 }/**
    105  *  录音声波监控定制器
    106  *
    107  *  @return 定时器
    108  */
    109 -(NSTimer *)timer{
    110     if (!_timer) {
    111         //创建一个定时器
    112         _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES];
    113     }
    114     return _timer;
    115 };
    116 
    117 /**
    118  *  录音声波状态设置
    119  */
    120 -(void)audioPowerChange{
    121     [self.audioRecorder updateMeters];//更新测量值
    122     float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0
    123     CGFloat progress=(1.0/160.0)*(power+160.0);
    124     [self.audioPower setProgress:progress];
    125 };
    126 #pragma mark - UI事件
    127 /**
    128  *  点击录音按钮
    129  *
    130  *  @param sender 录音按钮
    131  */
    132 - (IBAction)recordBu:(UIButton *)sender {
    133     if (![self.audioRecorder isRecording]) {
    134         [self.audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风,需要在info.plist加权限
    135        //创建一个定时器
    136         self.timer.fireDate=[NSDate distantPast];
    137     }
    138 };
    139 
    140 /**
    141  *  点击暂定按钮
    142  *
    143  *  @param sender 暂停按钮
    144  */
    145 - (IBAction)pauseBu:(UIButton *)sender {
    146     if ([self.audioRecorder isRecording]) {
    147         [self.audioRecorder pause];
    148         self.timer.fireDate=[NSDate distantFuture];
    149         NSLog(@"录音暂停");
    150     }
    151 };
    152 
    153 /**
    154  *  点击恢复按钮
    155  *  恢复录音只需要再次调用record,AVAudioSession会帮助你记录上次录音位置并追加录音
    156  *
    157  *  @param sender 恢复按钮
    158  */
    159 - (IBAction)resumeBu:(UIButton *)sender {
    160     [self recordBu:sender];
    161     NSLog(@"录音继续");
    162 };
    163 
    164 /**
    165  *  点击停止按钮
    166  *
    167  *  @param sender 停止按钮
    168  */
    169 - (IBAction)stopBu:(UIButton *)sender {
    170     [self.audioRecorder stop];
    171     self.timer.fireDate=[NSDate distantFuture];
    172     self.audioPower.progress=0.0;
    173     NSLog(@"录音文件路径%@",[self getSavePath]);
    174 };
    175 /**
    176  
    177     退出
    178  */
    179 -(IBAction)exitBu:(UIButton *)sender{
    180     exit(0);
    181 };
    182 #pragma mark - 录音机代理方法
    183 /**
    184  *  录音完成,录音完成后播放录音
    185  *
    186  *  @param recorder 录音机对象
    187  *  @param flag     是否成功
    188  */
    189 /*
    190 -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
    191     if (![self.audioPlayer isPlaying]) {
    192         [self.audioPlayer play];
    193     }
    194     NSLog(@"录音完成!");
    195 };
    196 */
    197 @end

    列表界面代码

      9 #import "ViewController2.h"
     10 #import <Foundation/Foundation.h>
     11 #import <AVFoundation/AVFoundation.h>
     12 #import "ViewController3.h"
     13 #import "Single.h"
     14 @interface ViewController2 () <AVAudioRecorderDelegate,UITableViewDelegate,UITableViewDataSource>
     15 //,UITableViewDelegate, UITableViewDataSource>
     16 //tableView
     17 @property (strong, nonatomic) IBOutlet UITableView *recoderTableView;
     18 
     19 @property (strong,nonatomic ) IBOutlet UIButton  *remove;
     20 
     21 
     22 
     23 
     24 @end
     25 
     26 
     27 @implementation ViewController2
     28 
     29 - (void)viewDidLoad {
     30     [super viewDidLoad];
     31     self.recoderTableView.dataSource=self;
     32     
     33 }
     34 
     35 //返回数组共下面的方法调用
     36 -(NSMutableArray *)getFileList{
     37     NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
     38     NSFileManager *fileManager = [NSFileManager defaultManager];
     39     //将doc下面的文件,以caf为后缀的全部存入数组
     40     //  NSMutableArray *ss=[fileManager enumeratorAtPath:urlStr];
     41     NSMutableArray *ss=[fileManager contentsOfDirectoryAtPath:urlStr error:nil];
     42      return  ss;
     43     // NSString *file;
     44 /* for(int i=0;i<[ss count];i++)
     45     while(file=[ss indexOfObject:i])
     46     {
     47         if([[file pathExtension] isEqual:@".caf"])
     48         {
     49             [fileList addObject:file];
     50             //看file是否有信息
     51             NSLog(@"%@",file);
     52         }
     53     }*/
     54 }
     55 //tableView 的相关方法
     56 //告诉控件分为几组
     57 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
     58 {
     59     return  1;
     60 }
     61 //告诉UI每组显示几行数据,section参数表示第几组,只有1组,那么section为0
     62 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
     63 {
     64     //一组数据,返回存储文件名称的数组长度
     65     return [[self getFileList]count];
     66 }
     67 //告诉控件每组的每行显示什么单元格内容
     68 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
     69 {
     70     UITableViewCell  *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
     71  //右侧的按钮
     72     cell.accessoryType=UITableViewCellAccessoryDetailButton;
     73     
     74     //获取存了文件信息的数组然后遍历赋值到cell文本中
     75    NSMutableArray *ss=[self getFileList];
     76     for(int i=0;i<[tableView numberOfRowsInSection:0];i++)
     77     {
     78      //   indexPath.section==0
     79     //控制每行的
     80       if( indexPath.row==i)
     81         cell.textLabel.text=[ss objectAtIndex:i];
     82     }
     83     return cell;
     84     
     85 }
     86 #pragma  ui控件操作
     87 -(IBAction)deleteBU:(UIButton *)sender{
     88     //setedit animate让tableView进入可编辑模式
     89     [_recoderTableView setEditing:!_recoderTableView.isEditing animated:true];
     90 }
     91 //对tableView自带删除功能的方法实现
     92 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
     93 {
     94     
     95     if (editingStyle == UITableViewCellEditingStyleDelete) {//删除
     96         NSFileManager *fileM=[NSFileManager new];
     97         NSString *newUrl2=[self cellItemPath:indexPath];
     98         [fileM removeItemAtPath:newUrl2 error:nil];
     99        // sing.singleCase=toUrl;
    100         [self.recoderTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];//删除刷新
    101     }
    102     
    103 }
    104 //tableView右侧的点击跳转,需要将URL通过单例对象送到第三个页面
    105 - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
    106 {
    107     NSString *newUrl2=[self cellItemPath:indexPath];
    108     NSURL *toUrl=[[NSURL alloc]initFileURLWithPath:newUrl2];
    109     NSLog(@"页面二准备复制给单例对象的URL是%@",toUrl);
    110     [Single sharedInstance].value=toUrl;
    111     //跳转代码
    112     UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:@"ViewController3"];
    113     [self presentModalViewController:next animated:NO];
    114 
    115 }
    116 //cell中拿到每行文件的信息,并且拼装成完整的路径,供删除当前行方法,和点击当前行跳转方法使用
    117 -(NSString  *)cellItemPath:(NSIndexPath *)indexPath{
    118     NSFileManager *fileM=[NSFileManager new];
    119     NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    120     //     NSString *newStr=urlStr+indexPath.row;
    121     NSArray *newArr=[fileM contentsOfDirectoryAtPath:urlStr error:nil];
    122     //行数
    123     int i=indexPath.row;
    124     //接收要删除的元素
    125     NSString *newPro;
    126     for(int j=0;j<[newArr count];j++)
    127     {
    128         if(j==i)
    129         {
    130             newPro= [newArr objectAtIndex:i];
    131         }
    132     }
    133     //重新拼接路径,路径文件夹名+/+要删除  的文件名
    134     NSString *newUrl=[urlStr stringByAppendingString:@"/"];
    135     NSString *newUrl2=[newUrl stringByAppendingString:newPro];
    136     return  newUrl2;
    137 }
    138 
    139 
    140 
    141 @end

    播放界面代码

    #import "ViewController3.h"
    #import <AVFoundation/AVFoundation.h>
    #import "ViewController.h"
    #import "ViewController2.h"
    #import "Single.h"
    @interface ViewController3 ()<AVAudioRecorderDelegate>
    @property (nonatomic,strong) NSTimer *timer;  //声波监控,对播放进行监控
    @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音频波动
    @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音频播放器,用于播放录音文件
    @property (weak,nonatomic ) IBOutlet UIButton *pause;//暂停
    @property (weak,nonatomic ) IBOutlet UIButton *conti;//继续
    @property (weak,nonatomic ) IBOutlet UIButton *back;//返回
    //通过单例对象拿到第二个控制器的cell对象传的URL
    @property (nonatomic,copy) NSURL *url;
    
    @property (weak, nonatomic) IBOutlet UIButton *exit0;
    
    @end
    
    @implementation ViewController3
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        _url=[Single sharedInstance].value;
        
        NSLog(@"页面三%@",_url);
        if(!_audioPlayer){
        _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:_url error:nil];
        _audioPlayer.numberOfLoops=0;
        [_audioPlayer prepareToPlay];
        
        //开启定时器
        self.timer.fireDate=[NSDate distantPast];
        }
         
        [self setAudioSession];
    [self.audioPlayer play]; }
    /** * 设置音频会话 */ -(void)setAudioSession{ AVAudioSession *audioSession=[AVAudioSession sharedInstance]; //设置为播放状态 [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; } /** * 录音声波监控定制器 * * @return 定时器 */ -(NSTimer *)timer{ if (!_timer) { //创建一个定时器 _timer=[NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES]; } return _timer; }; /** * 播放声波状态设置 */ -(void)audioPowerChange{ float progress= self.audioPlayer.currentTime /self.audioPlayer.duration; [self.audioPower setProgress:progress animated:true]; }; #pragma UI操作 //退出 -(IBAction)exitBu:(UIButton *)sender{ exit(0); }; //返回 -(IBAction)backBu:(UIButton *)sender{ UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:@"ViewController2"]; [self presentModalViewController:next animated:NO]; }; //暂停 -(IBAction)pauseBu:(UIButton *)sender { [self.audioPlayer pause]; self.timer.fireDate=[NSDate distantFuture]; }; //继续 -(IBAction)contiBu:(UIButton *)sender{ [self.audioPlayer play]; self.timer.fireDate=[NSDate distantPast]; } @end

    还有个单例类存URL的代码,这个我写过了,在随笔里

  • 相关阅读:
    在VMware中使用Nat方式设置静态IP
    saltstack实现自动化扩容
    saltstack常用模块
    saltstack之nginx、php的配置
    桶排序
    【前端安全】JavaScript防http劫持与XSS
    memcached
    10 行 Python 代码写的模糊查询
    为什么print在python3中变成了函数?
    一行python代码实现树结构
  • 原文地址:https://www.cnblogs.com/kc1995/p/13162670.html
Copyright © 2011-2022 走看看