美文网首页
iOS播放和录制音频

iOS播放和录制音频

作者: 玄裳 | 来源:发表于2017-02-22 19:14 被阅读0次

音频会话分类


AV Foundation 定义了 7 种分类来描述应用程序所使用的音频行为。

音频会话分类.png

配置音频会话


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //配置音频会话
    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *error ;
    
    if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
        NSLog(@"category error : %@",[error localizedDescription]);
    }
    
    if (![session setActive:YES error:&error]) {
        NSLog(@"Activation error : %@",[error localizedDescription]);
    }
    
    return YES;
}

使用AVAudioPlayer播放音频


创建 AVAudioPlayer

@interface ViewController ()

//必须强引用,不然没声音
@property (nonatomic, strong) AVAudioPlayer *player;

@end

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];

   NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"算什么男人" withExtension:@"mp3"];
   
   self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
   
   //修改音量
   self.player.volume = 0.1;
   
   //立体播放声音
   self.player.pan = 0.5;
   
   //调整播放率
   self.player.enableRate = YES;
   //快速/慢速
   self.player.rate = 1.5;
   
   //无缝循环,大于0的数,是可以实现播放器n次循环, 设置为-1,则为无限循环。
   self.player.numberOfLoops = 2;
   
   if (self.player) {
       
       //预加载,可降低调用Play方法和听到声音输出之间的延时(可选的)
       [self.player prepareToPlay];
      
       //会隐性激活prepareToPlay方法
       [self.player play];
   }
   
}

@end

调用 Play 方法可以实现立即播放音频的功能,pause 方法可以对播放暂停,stop 可以停止播放行为,并且撤销 prepareToPlay 时所做的设置,而pause方法则不会。

处理中断事件


#import "DLAudioPlayer.h"
#import <AVFoundation/AVFoundation.h>


@protocol DLPlayerControllerDelegate <NSObject>

- (void)playbackStopped;
- (void)playbackBegan;

@end

@interface DLAudioPlayer ()
//必须强引用,不然没声音
@property (nonatomic, strong) AVAudioPlayer *player;
@property (nonatomic, copy) NSArray *players;
@property (nonatomic) BOOL isPlaying;
@property (nonatomic, weak) id <DLPlayerControllerDelegate> delegate;

@end

@implementation DLAudioPlayer
- (instancetype)init
{
    self = [super init];
    if (self) {
        
        AVAudioPlayer *player1 = [self playForFile:@"算什么男人"];
        AVAudioPlayer *player2 = [self playForFile:@"安静"];
        AVAudioPlayer *player3 = [self playForFile:@"说好的幸福呢"];
        
        _players = @[player1,player2, player3];
        
        //注册处理中断通知
        NSNotificationCenter *nc= [NSNotificationCenter defaultCenter];
        
        [nc addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
        
        // //注册线路改变通知
        [nc addObserver:self selector:@selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
        
    }
    return self;
}

- (AVAudioPlayer *)playForFile:(NSString *)name
{
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:name withExtension:@"mp3"];
    
    NSError *error ;
    
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
    
    if (player) {
        player.numberOfLoops = -1;
        player.enableRate = YES;
        [player prepareToPlay];
        [player play];
    }
    
    else
    {
        NSLog(@"error : @",[error localizedDescription]);
    }
    
    return player;
}

//三个播放器进行同步 需要捕捉当前设备时间并添加一个延时
- (void)play
{
    if (!_isPlaying) {
        NSTimeInterval delayTime  = [self.players[0] deviceCurrentTime] +0.01;
        
        for (AVAudioPlayer *player in self.players) {
            [player playAtTime:delayTime];
        }
        
        self.isPlaying = YES;
    }
}

//停止播放
- (void)stop
{
    if (_isPlaying) {
        
        for (AVAudioPlayer *player in self.players) {
            [player stop];
            
            //播放进度回到原点
            player.currentTime = 0;
        }
        
        self.isPlaying = NO;
    }
}

//调整播放速率
- (void)adjustRate:(float)rate
{
    for (AVAudioPlayer *player in self.players) {
        
        player.rate = rate;
    }
}

//调整音量
- (void)adjustVolume:(float)volume forPlayerAtIndex:(NSUInteger)index{
    
    if ([self isValidIndex:index]) {
        
        AVAudioPlayer *player = self.players[index];
        player.volume = volume;
    }
    
}

//调整立体声
- (void)adjustPan:(float)Pan forPlayerAtIndex:(NSUInteger)index{
    
    if ([self isValidIndex:index]) {
        
        AVAudioPlayer *player = self.players[index];
        player.pan = Pan;
    }
    
}

- (BOOL)isValidIndex:(NSUInteger)index
{
    return index == 0 || index < self.players.count;
}

- (void)handleInterruption:(NSNotification *)notification
{
    //收到通知 处理中断事件
    NSDictionary *info = notification.userInfo;
    
    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    
    if (type == AVAudioSessionInterruptionTypeBegan) {
        
        //执行中断开始事件
        [self stop];
        
        if (self.delegate) {
            
            [self.delegate playbackStopped];
        }
    }
    
    else
    {
        //执行中断结束事件
        
        AVAudioSessionCategoryOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
        
        //会话是否重新激活以及是否可以再次播放
        if (options == AVAudioSessionInterruptionOptionShouldResume) {
            
            [self play];
            
            if (self.delegate) {
                [self.delegate playbackBegan];
            }
        }
    }
}

- (void)handleRouteChange:(NSNotification *)notification
{
    NSDictionary *info = notification.userInfo;
    
    AVAudioSessionRouteChangeReason reson = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
    
    //设备断开时停止播放
    if (reson == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
       
        AVAudioSessionRouteDescription *previousRoute = info[AVAudioSessionRouteChangePreviousRouteKey];
        
        AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
        
        NSString *portType = previousOutput.portType;
        
        if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
            [self stop];
            [self.delegate playbackStopped];
        }
    }
}
//销毁通知
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


@end

使用AVAudioPlayer录制音频


创建 AVAudioRecorder

@interface ViewController ()

@property (nonatomic, strong) AVAudioRecorder *recorder;

@end



@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];

    //文件路径
    NSString *directory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath  = [directory stringByAppendingPathComponent:@"voice.m4a"];
    
    NSURL *url  = [NSURL fileURLWithPath:filePath];
   
    //配置录音会话键值 第一个键值是音频格式, 第二个键值是采样率 第三个键值是通道数
    NSDictionary *dic = @{AVFormatIDKey:@(kAudioFormatMPEG4AAC),
    AVSampleRateKey:@22050.f,AVNumberOfChannelsKey:@1};


    NSError *error;
    
    self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:dic error:&error];
    
    if (self.recorder) {
        [self.recorder prepareToRecord];
        [self.recorder record];
    }
    else
    {
        // 处理error
    }
    
}

音频格式:

CF_ENUM(AudioFormatID)
{
    kAudioFormatLinearPCM               = 'llcm',//未压缩的饮品,保真度最高,但文件也很大
    kAudioFormatAC3                     = 'ac-3',
    kAudioFormat60958AC3                = 'cac3',
    kAudioFormatAppleIMA4               = 'ima4',//会缩小文件,但能保证高质量的音频内容
    kAudioFormatMPEG4AAC                = 'aac ',//会缩小文件,但能保证高质量的音频内容
    kAudioFormatMPEG4CELP               = 'celp',
    kAudioFormatMPEG4HVXC               = 'hvxc',
    kAudioFormatMPEG4TwinVQ             = 'twvq',
    kAudioFormatMACE3                   = 'MAC3',
    kAudioFormatMACE6                   = 'MAC6',
    kAudioFormatULaw                    = 'ulaw',
    kAudioFormatALaw                    = 'alaw',
    kAudioFormatQDesign                 = 'QDMC',
    kAudioFormatQDesign2                = 'QDM2',
    kAudioFormatQUALCOMM                = 'Qclp',
    kAudioFormatMPEGLayer1              = '.mp1',
    kAudioFormatMPEGLayer2              = '.mp2',
    kAudioFormatMPEGLayer3              = '.mp3',
    kAudioFormatTimeCode                = 'time',
    kAudioFormatMIDIStream              = 'midi',
    kAudioFormatParameterValueStream    = 'apvs',
    kAudioFormatAppleLossless           = 'alac',
    kAudioFormatMPEG4AAC_HE             = 'aach',
    kAudioFormatMPEG4AAC_LD             = 'aacl',
    kAudioFormatMPEG4AAC_ELD            = 'aace',
    kAudioFormatMPEG4AAC_ELD_SBR        = 'aacf',
    kAudioFormatMPEG4AAC_ELD_V2         = 'aacg',    
    kAudioFormatMPEG4AAC_HE_V2          = 'aacp',
    kAudioFormatMPEG4AAC_Spatial        = 'aacs',
    kAudioFormatAMR                     = 'samr',
    kAudioFormatAMR_WB                  = 'sawb',
    kAudioFormatAudible                 = 'AUDB',
    kAudioFormatiLBC                    = 'ilbc',
    kAudioFormatDVIIntelIMA             = 0x6D730011,
    kAudioFormatMicrosoftGSM            = 0x6D730031,
    kAudioFormatAES3                    = 'aes3',
    kAudioFormatEnhancedAC3             = 'ec-3'
};

Tips: 指定的音频格式一定要和 URL 参数定义的文件类型兼容。

录制并播放语音


先在 AppDelegate 中修改音频分类:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //配置音频会话
    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *error ;
    
    //使用PlayAndRecord分类
    if (![session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]) {
        NSLog(@"category error : %@",[error localizedDescription]);
    }
    
    if (![session setActive:YES error:&error]) {
        NSLog(@"Activation error : %@",[error localizedDescription]);
    }
    
    return YES;
}

实现录音功能:

#import "DLAudioRecorder.h"
#import <AVFoundation/AVFoundation.h>
#import "DLMemo.h"

typedef void(^DLRecordingStopCompletionHandler)(BOOL);
typedef void(^DLRecordingSaveCompletionHandler)(BOOL,id);


@interface DLAudioRecorder ()<AVAudioRecorderDelegate>

@property (nonatomic, readonly) NSString *formattedCurrentTime;

@property (nonatomic, strong) AVAudioPlayer *player;
@property (nonatomic, strong) AVAudioRecorder *recorder;
@property (nonatomic, strong) DLRecordingStopCompletionHandler completionHandler;
@end


@implementation DLAudioRecorder

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        //实例化录音器
        NSString *tmpStr = NSTemporaryDirectory();
        NSString *filePath = [tmpStr stringByAppendingPathComponent:@"memo.caf"];
        
        NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
        
        NSDictionary *setting = @{AVFormatIDKey:@(kAudioFormatAppleIMA4),AVSampleRateKey:@44100.0f,AVNumberOfChannelsKey:@1,AVEncoderBitDepthHintKey:@16,AVEncoderAudioQualityKey:@(AVAudioQualityMedium)};
        
        NSError *error;
        
        self.recorder = [[AVAudioRecorder alloc] initWithURL:fileUrl settings:setting error:&error];
        
        if (self.recorder) {
            self.recorder.delegate = self;
            [self.recorder prepareToRecord];
            [self.recorder record];
        }
        
        else
        {
            NSLog(@"error: %@",[error localizedDescription]);
        }
    }
    return self;
}

- (BOOL)record{
    
    return [self.recorder record];
}

- (void)pause{
    [self.recorder pause];
}

//委托给录音器实现停止操作
- (void)stopWithCompletionHandler:(DLRecordingStopCompletionHandler)handler{
    self.completionHandler = handler;
    [self.recorder stop];
}

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
    if (self.completionHandler) {
        self.completionHandler(flag);
    }
}

//保存录音
- (void)saveRecordingWithName:(NSString *)name completionHandler:(DLRecordingSaveCompletionHandler)handler{
    
    NSTimeInterval timesTamp = [NSDate timeIntervalSinceReferenceDate];
    
    NSString *filename = [NSString stringWithFormat:@"%@,%f",name,timesTamp];
    
    NSString *docsDirectory = [self documentDirectory];
    
    NSString *destPath = [docsDirectory stringByAppendingPathComponent:filename];
    
    NSURL *srcUrl = self.recorder.url;
    
    NSURL *destUrl = [NSURL fileURLWithPath:destPath];
    
    NSError *error;
    
    BOOL success = [[NSFileManager defaultManager] copyItemAtURL:srcUrl toURL:destUrl error:&error];
    
    if (success) {
        handler(YES,[DLMemo memoWithTitle:name url:destUrl]);
    }
    
    else
    {
        handler(NO,error);
    }

}

//查找路径
- (NSString *)documentDirectory
{
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
}

//播放备忘录
- (BOOL)playbackMemo:(DLMemo *)memo
{
    [self.player stop];
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:memo.url error:nil];
    
    if (self.player) {
    
        [self.player play];
        return YES;
    }
    
    return NO;
}


- (NSString *)formattedCurrentTime
{
    NSUInteger time = (NSUInteger) self.recorder.currentTime;
    NSInteger hours = (time/3600);
    NSInteger minutes = (time/60) % 60;
    NSInteger seconds = time % 60;
    
    NSString *format = @"%02i:%02i:%02i";
    
    return [NSString stringWithFormat:format,hours,minutes,seconds];
}

相关文章

网友评论

      本文标题:iOS播放和录制音频

      本文链接:https://www.haomeiwen.com/subject/msimwttx.html