ios 音频录音、上传至7牛、播放及其与android兼容的问题

本文介绍iOS平台下使用AVAudioRecorder实现录音功能的方法,并通过AVPlayer实现音频播放。文章涵盖录音提示音、动态声强展示及计时等功能的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

iOS录音使用AVAudioRecorder,播放用 AVPlayer 就可以很好的解决,网上也有很多的教程。这里就不细讲,后面会附上代码。

先说一下demo内容,现在项目要求做一个录音、上传、播放的功能。细节:录音前需要提示音,录音时会有一个根据声音强度来展示的相应动态效果,还有个计时功能。

我们来说一下我在制作此功能时所遇到的坑!!

这里先呈现音频录制的代码,这里面引用了SpectrumView点击打开链接。这是一个根据声强来显示声音录制的效果,如果不需要可以去除。

录制音频代码:

1.首先说一下录制音频的格式。

   经验证,这里博主建议各位使用AAC的录制格式。AAC(Advanced Audio Coding),中文称为“高级音频编码”,所占内存小,音质也不错,最重要的是可以与android兼容。


2.这里用的是7牛云的上传。这里我遇到的大问题就是上传音频到7牛云后,发现格式不能识别,显示为application/octet-stream。我最开始用.mp4的格式,的确上传上去后,能够识别为video/mp4,并且能正常播放。 但是后来发现不能播放android的录音。于是就想办法解决,经过一下午的各种博客浏览,。。。无果!! 好吧,最后想到先本地存储,再来播放的方式,解决问题。(这里的录音文件都比较小,不超过30s)


这里附上部分demo片段,有需要自取。

录音部分:#import "AudioRecorderVC.h"
#import <AVFoundation/AVFoundation.h>
#define kRecordAudioFile @"myRecord.aac"

@interface AudioRecorderVC ()<AVAudioRecorderDelegate>
@property (strong,nonatomic) SpectrumView *spectrumView;
@property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音频录音机
@property (nonatomic, strong) AVAudioPlayer *bellplayer;
@property (nonatomic, assign) int sCountup;
@property (nonatomic, strong) NSTimer *mTimer;
@end

@implementation AudioRecorderVC

#pragma mark - 控制器视图方法
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self.naView setHidden:YES];
[self.statusTip setHidden:YES];
[self.view createBordersWithColor:[UIColor clearColor] withCornerRadius:6 andWidth:1];
[self.labReminder createBordersWithColor:[UIColor clearColor] withCornerRadius:4 andWidth:1];
[self.labReminder setTextColor:MCOLOR_FFFFFF];
[self addTapGesture];
[self addSpectrumView];
[self labToSize];
}

-(void)addSpectrumView{
if (!self.spectrumView) {
__weak AudioRecorderVC *weakSelf = self;
self.spectrumView = [[SpectrumView alloc] initWithFrame:CGRectMake(CGRectGetMidX(self.view.bounds)-150,240,300, 60.0)];
self.spectrumView.hidden = YES;
self.spectrumView.text = [NSString stringWithFormat:@"%d",0];
__weak SpectrumView * weakSpectrum = self.spectrumView;
self.spectrumView.itemLevelCallback = ^() {

[weakSelf.audioRecorder updateMeters];
//取得第一个通道的音频,音频强度范围是-160到0
float power= [weakSelf.audioRecorder averagePowerForChannel:0];
weakSpectrum.level = power;
};
[self.view addSubview:self.spectrumView];
}
}

-(void)addTapGesture{
//添加手势
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickPop)];
//将手势添加到需要相应的view中去
[self.view addGestureRecognizer:tapGesture];
}

-(void)clickPop{
if (_Block) {
_Block(nil);
}
}

#pragma mark - getter 懒加载
- (UIButton *)btnRecorder {
// 开始
[_btnRecorder addTarget:self action:@selector(recordStart:) forControlEvents:UIControlEventTouchDown];
// 取消
// [_btnRecorder addTarget:self action:@selector(recordCancel:) forControlEvents: UIControlEventTouchUpOutside];
//完成
[_btnRecorder addTarget:self action:@selector(recordFinish:) forControlEvents:UIControlEventTouchUpInside];
/*
//移出
[_btnRecorder addTarget:self action:@selector(recordTouchDragExit:) forControlEvents:UIControlEventTouchDragExit];
//移入
[_btnRecorder addTarget:self action:@selector(recordTouchDragEnter:) forControlEvents:UIControlEventTouchDragEnter];
*/
return _btnRecorder;
}

/**
* 获得录音机对象
*
* @return 录音机对象
*/
- (AVAudioRecorder *)audioRecorder {
if (!_audioRecorder) {
[self setAudioSession];
//创建录音文件保存路径
NSURL *url=[self getSavePath];
//创建录音格式设置
NSDictionary *setting=[self getAudioSetting];
//创建录音机
NSError *error=nil;
_audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
_audioRecorder.delegate= self;
_audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES
if (error) {
NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
return nil;
}
}
return _audioRecorder;
}

#pragma mark - layout

- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat width = self.view.bounds.size.width;
CGFloat height = self.view.bounds.size.height;
self.btnRecorder.frame = CGRectMake(width / 2.f - 50.f, height - 180.f, 100.f, 100.f);
[self.audioRecorder record];
[self.audioRecorder stop];
[self removeFile];
}

#pragma mark - ControlEvents
/*
- (void)recordCancel:(UIButton *)button {

if ([self.audioRecorder isRecording]) {
NSLog(@"取消");
[self.audioRecorder stop];
self.spectrumView.hidden = NO;
}
}
*/
- (void)recordStart:(UIButton *)button {
if (![self.audioRecorder isRecording]) {
NSLog(@"录音开始");
[self startScount];
[self playthebell];
[self.audioRecorder record];
[self startAnimate];
self.labReminder.hidden = YES;
self.spectrumView.hidden = NO;

}
}

- (void)recordFinish:(UIButton *)button {

if ([self.audioRecorder isRecording]) {
NSLog(@"完成");
[self.audioRecorder stop];
[self stopAnimate];
self.spectrumView.hidden = NO;
[self judgePushAudio];
}
}
/*
- (void)recordTouchDragExit:(UIButton *)button {
if([self.audioRecorder isRecording]) {
[self stopAnimate];
}
}

- (void)recordTouchDragEnter:(UIButton *)button {
if([self.audioRecorder isRecording]) {
[self startAnimate];
}
}
*/
- (void)startAnimate {
[self.spectrumView start];
}

- (void)stopAnimate {
[self.spectrumView stop];
[self.mTimer invalidate];
self.mTimer = nil;
}

- (void)setAudioSession {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *sessionError;
//AVAudioSessionCategoryPlayAndRecord用于录音和播放
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
if(session == nil)
NSLog(@"Error creating session: %@", [sessionError description]);
else
[session setActive:YES error:nil];
}

/**
* 取得录音文件设置
*
* @return 录音设置
*/
- (NSDictionary *)getAudioSetting {
NSMutableDictionary *dicM=[NSMutableDictionary dictionary];
//设置录音格式
[dicM setObject:@(kAudioFormatMPEG4AAC) forKey:AVFormatIDKey];
//设置录音采样率,8000是电话采样率,对于一般录音已经够了
[dicM setObject:@(8000) forKey:AVSampleRateKey];
//设置通道,这里采用单声道
[dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
//每个采样点位数,分为8、16、24、32
[dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
//是否使用浮点数采样
[dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
//....其他设置等

return dicM;
}

/**
* 取得录音文件保存路径
*
* @return 录音文件路径
*/
- (NSURL *)getSavePath {

// 在Documents目录下创建一个名为FileData的文件夹
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"AudioData"];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDir = FALSE;
BOOL isDirExist = [fileManager fileExistsAtPath:path isDirectory:&isDir];
if(!(isDirExist && isDir)) {
BOOL bCreateDir = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
if(!bCreateDir){
NSLog(@"创建文件夹失败!");
}
NSLog(@"创建文件夹成功,文件路径%@",path);
}
path = [path stringByAppendingPathComponent:kRecordAudioFile];
NSLog(@"file path:%@",path);
NSURL *url=[NSURL fileURLWithPath:path];

return url;
}

- (void)removeFile{
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"AudioData"];
NSFileManager *fileManager = [NSFileManager defaultManager];
path = [path stringByAppendingPathComponent:kRecordAudioFile];
NSError *error;
if ([fileManager removeItemAtPath:path error:&error] != YES)
NSLog(@"Unable to delete file: %@", [error localizedDescription]);

}

- (void)judgePushAudio{
if (_sCountup < 1) {
[self showToast:@"录音时间太短,请重试!"];
[self removeFile];

}else if(_sCountup >= 1 && _sCountup <= 30){
if (_Block) {
_Block([self getSavePath]);
}
}
}

- (void)labToSize{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:self.labReminder.text];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:20.0f];//调整行间距
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [self.labReminder.text length])];
self.labReminder.attributedText = attributedString;
[self.labReminder sizeToFit];
}

#pragma --提示音
- (void)playthebell{
NSString *mp3Str;
mp3Str = @"talkroom_begin";
NSString *filePath = [[NSBundle mainBundle] pathForResource:mp3Str ofType:@"mp3"];
self.bellplayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:nil];
self.bellplayer.volume = 1.0;
self.bellplayer.numberOfLoops = -1;
[self.bellplayer prepareToPlay];
[self.bellplayer play];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, ( 0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.bellplayer stop];
});
}

#pragma --计时器
- (void)startScount{
self.sCountup = 0;
[UIView animateWithDuration:0.5 animations:^{
self.mTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(startCountUp) userInfo:nil repeats:YES];
}];
}

- (void)startCountUp{
_sCountup++;
self.spectrumView.timeLabel.text = [NSString stringWithFormat:@"%ds", _sCountup];
if (_sCountup == 30) {
[self recordFinish:nil];
}
}

@end

3
#pragma --播放录音
- (void)addVoiceButton:(DoorVoucherCell *)cell{
    UIButton *voiceBtn = [[UIButton alloc]initWithFrame:CGRectMake(cell.contentValue.left, 0, 150, 50)];
    [voiceBtn addTarget:self action:@selector(playRecoderVoice) forControlEvents:UIControlEventTouchUpInside];
    voice = [[UIImageView alloc]initWithFrame:CGRectMake(0, 12, 25, 25)];
    //动画未开始前的图片
    voice.image = [UIImage imageNamed:@"chat_animation_white3"];
    //进行动画效果的3张图片(按照播放顺序放置)
    voice.animationImages = [NSArray arrayWithObjects:
                             [UIImage imageNamed:@"chat_animation_white1"],
                             [UIImage imageNamed:@"chat_animation_white2"],
                             [UIImage imageNamed:@"chat_animation_white3"],nil];
    //设置动画间隔
    voice.animationDuration = 1;
    voice.animationRepeatCount = 0;
    voice.userInteractionEnabled = NO;
    voice.backgroundColor = [UIColor clearColor];
    [voiceBtn addSubview:voice];
    [cell addSubview:voiceBtn];
}

- (NSURL *)writeRecoderToFile{
    //播放
    NSURL *url = [NSURL URLWithString:self.dataModel.voice];
    //把音频文件保存到本地
    NSData *audioData = [NSData dataWithContentsOfURL:url];
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"AudioData"];
    path = [path stringByAppendingPathComponent:@"myRecord.aac"];
    //  DDLogWarn(@" 从网络拿到的音频数据写入的本地路径  %@",filePath);
    [audioData writeToFile:path atomically:YES];
    
    NSURL *fileURL = [NSURL fileURLWithPath:path];
    return fileURL;
}

- (void)playRecoderVoice{
    [self setAudioPlayer];
    [self.audioPlayer play];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [voice startAnimating];
    });
}

- (void)playerItemDidReachEnd{
    [self playthebell];
    [voice stopAnimating];
    voice.image = [UIImage imageNamed:@"chat_animation_white3"];
}



- (AVPlayer *)setAudioPlayer{
    NSError *error=nil;
    AVPlayerItem * songItem = [[AVPlayerItem alloc]initWithURL:[self writeRecoderToFile]];
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setActive:YES error:nil];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    _audioPlayer = [[AVPlayer alloc] initWithPlayerItem:songItem];
    // 监听音乐是否播放完成
    
    [[NSNotificationCenter defaultCenter] addObserver:self
     
                                             selector:@selector(playerItemDidReachEnd)
     
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
     
                                               object:nil];
    if (error) {
        NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription);
        return nil;
    }
    return _audioPlayer;
}

#pragma --提示音
- (void)playthebell{
    NSString *mp3Str;
    mp3Str = @"talkroom_up";
    NSString *filePath = [[NSBundle mainBundle] pathForResource:mp3Str ofType:@"mp3"];
    self.bellplayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:nil];
    self.bellplayer.volume = 1.0;
    self.bellplayer.numberOfLoops = -1;
    [self.bellplayer prepareToPlay];
    [self.bellplayer play];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, ( 0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.bellplayer stop];
    });
}






有问题欢迎提问,喜欢请点赞,Star。谢谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值