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

配置音频会话
- (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];
}
网友评论