iOS 录音,播放并上传

2023-05-16

1.界面布局,以及相关功能

点击中间开始录音,点击左上角播放或暂停播放,点击右上角移除文件


2.定义相关属性


#import "SendVoiceController.h"

#import#import "RecordVoiceView.h"

#import "lame.h"

#import "PlayVoiceView.h"

#define cafFilePathName @"myRecordForCaf.caf"

#define mp3FilePathName @"myRecordForMp3.mp3"

@interface SendVoiceController ()//录音存储路径

@property (nonatomic, strong)NSURL *tmpFile;

//录音

@property (nonatomic, strong)AVAudioRecorder *recorder;

//播放

@property (nonatomic, strong)AVAudioPlayer *player;

//录音动画

@property(nonatomic,strong)RecordVoiceView *recordview;

@property(strong,nonatomic)PlayVoiceView *playview;

//录音计时器

@property(nonatomic,strong)NSTimer *recordTimer;

//录音秒数

@property(strong,nonatomic)NSTimer *timer;

@property(nonatomic,assign)int index;

@property(nonatomic,assign)int duration;

@end

@implementation SendVoiceController

3.创建audiosession

-(void)setAVRecorder{

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

urlStr=[urlStr stringByAppendingPathComponent:cafFilePathName];

self.tmpFile = [NSURL fileURLWithPath:urlStr];

//设置后台播放

AVAudioSession *session = [AVAudioSession sharedInstance];

NSError *sessionError;

//AVAudioSessionCategoryPlayAndRecord,这个确保了既能录音也能播放

[session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];

//判断后台有没有播放

if (session == nil) {

NSLog(@"Error creating sessing:%@", [sessionError description]);

} else {

[session setActive:YES error:nil];

}

}

4.中间图片手势代理方法

//长按事件的实现方法

- (void) handleTableviewCellLongPressed:(UILongPressGestureRecognizer *)gestureRecognizer {

if (gestureRecognizer.state ==

UIGestureRecognizerStateBegan) {

NSLog(@"UIGestureRecognizerStateBegan");

[self recorderPressing];

}

if (gestureRecognizer.state ==

UIGestureRecognizerStateChanged) {

NSLog(@"UIGestureRecognizerStateChanged");

}

if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {

NSLog(@"UIGestureRecognizerStateEnded");

[self recorderEnd];

}

}


5.创建AVAudioRecorder并开始录音

-(void)recorderPressing{

NSDictionary *settingdic = [NSDictionary dictionaryWithObjectsAndKeys:

[NSNumber numberWithFloat:8000],                  AVSampleRateKey, // 电话所用采样率

[NSNumber numberWithInt:kAudioFormatLinearPCM],    AVFormatIDKey,

[NSNumber numberWithInt:2],                      AVNumberOfChannelsKey,

[NSNumber numberWithInt:16],                      AVLinearPCMBitDepthKey,

[NSNumber numberWithInt:AVAudioQualityMin],      AVEncoderAudioQualityKey,

nil];

//开始录音,将所获取到得录音存到文件里

//开始录音,将所获取到得录音存到文件里

self.recorder = [[AVAudioRecorder alloc] initWithURL:_tmpFile settings:settingdic error:nil];

self.recorder.meteringEnabled = YES;

self.recorder.delegate=self;

//准备记录录音

[self.recorder prepareToRecord];

//启动或者恢复记录的录音文件

[self.recorder record];

self.player = nil;

//以下是我仿照卫星发语音的动画效果

self.recordview.hidden=NO;

self.recordTimer=[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(recordTimerChange) userInfo:nil repeats:YES];

}

6.中间图片长按取消的同时关闭录音

-(void)recorderEnd{

self.duration=self.index;

self.index=0;

//停止录音

if (self.recordTimer) {

[self.recordTimer invalidate];

self.recordTimer=nil;

}

[self.recorder stop];

self.recordview.hidden=YES;

self.recorder = nil;

self.playBtn.hidden=NO;

self.removeBtn.hidden=NO;

self.curveLineImage.hidden=NO;

NSError *playError;

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

urlStr=[urlStr stringByAppendingPathComponent:mp3FilePathName];

NSURL *url = [NSURL fileURLWithPath:urlStr];

self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&playError];

//当播放录音为空, 打印错误信息

if (self.player == nil) {

NSLog(@"Error crenting player: %@", [playError description]);

}

self.player.delegate = self;

//    [self.player play];

}


7.与其同时触发avaudiorecord代理方法

这里面的主要作用是把录音的caf文件转成mp3文件,用于上传到服务器,其中的方法是来自于Libmo3lame库

#pragma mark---录音代理

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *cafFilePath =[urlStr stringByAppendingPathComponent:cafFilePathName];    //caf文件路径

NSString *mp3FilePath = [urlStr stringByAppendingPathComponent:mp3FilePathName]; ;//存储mp3文件的路径

NSFileManager * fileManager=[ NSFileManager defaultManager ];

if ([fileManager fileExistsAtPath:cafFilePath]){

float msize= [[fileManager attributesOfItemAtPath:cafFilePath error:nil] fileSize]/(1024.0*1024.0);

NSLog(@"caf文件大小%lf",msize);

}else{

float msize= [[fileManager attributesOfItemAtPath:mp3FilePathName error:nil] fileSize]/(1024.0*1024.0);

NSLog(@"mp3文件大小%lf",msize);

}

if ([fileManager removeItemAtPath :mp3FilePath error : nil ])

{

NSLog ( @" 删除 " );

}

@try {

int read, write;

FILE *pcm = fopen ([cafFilePath cStringUsingEncoding : 1 ], "rb" );  //source 被 转换的音频文件位置

if (pcm == NULL )

{

NSLog ( @"file not found" );

}

else

{

fseek (pcm, 4 * 1024 , SEEK_CUR );                                  //skip file header

FILE *mp3 = fopen ([mp3FilePath cStringUsingEncoding : 1 ], "wb" );  //output 输出生成的 Mp3 文件位置

const int PCM_SIZE = 8192 ;

const int MP3_SIZE = 8192 ;

short int pcm_buffer[PCM_SIZE* 2 ];

unsigned char mp3_buffer[MP3_SIZE];

lame_t lame = lame_init ();

lame_set_num_channels (lame, 2 ); // 设置 1 为单通道,默认为 2 双通道

lame_set_in_samplerate (lame,  8000.0 ); //11025.0

//lame_set_VBR(lame, vbr_default);

lame_set_brate (lame, 16);

lame_set_mode (lame, 3 );

lame_set_quality (lame, 2 ); /* 2=high 5 = medium 7=low 音 质 */

lame_init_params (lame);

do {

read = 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);

fwrite (mp3_buffer, write, 1 , mp3);

} while (read != 0 );

lame_close (lame);

fclose (mp3);

fclose (pcm);

return  ;

}

return  ;

}

@catch (NSException *exception) {

NSLog ( @"%@" ,[exception description ]);

return  ;

}

@finally {

NSLog ( @" 执行完成 " );

}

}

8.左上角按钮事件

-(IBAction)playerClick:(id)sender{

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString * path=[urlStr stringByAppendingPathComponent:cafFilePathName];

NSFileManager* manager = [NSFileManager defaultManager];

if ([manager fileExistsAtPath:path]){

float msize= [[manager attributesOfItemAtPath:path error:nil] fileSize]/(1024.0*1024.0);

NSLog(@"文件大小%lf",msize);

}

else{

_player=nil;

}

//判断是否正在播放,如果正在播放

if ([self.player isPlaying]) {

//暂停播放

[_player pause];

[self.playBtn  setImage:[UIImage imageNamed:@"voice_play"] forState:UIControlStateNormal];

//按钮显示为播放

} else {

//开始播放

[_player play];

[self.playBtn  setImage:[UIImage imageNamed:@"voice_pause"] forState:UIControlStateNormal];

}

}


9.右上角按钮事件

-(IBAction)removeClick:(id)sender{

NSIndexPath *indexpath=[NSIndexPath indexPathForItem:6 inSection:0];

self.playBtn.hidden=YES;

self.removeBtn.hidden=YES;

self.curveLineImage.hidden=YES;

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *cafFilePath =[urlStr stringByAppendingPathComponent:cafFilePathName];    //caf文件路径

NSString *mp3FilePath = [urlStr stringByAppendingPathComponent:mp3FilePathName]; ;//存储mp3文件的路径

NSFileManager * fileManager=[ NSFileManager defaultManager ];

if ([fileManager removeItemAtPath :mp3FilePath error : nil ])

{

NSLog ( @" 删除 mp3" );

}

if ([fileManager removeItemAtPath :cafFilePath error : nil ])

{

NSLog ( @" 删除 caf" );

}

}

10.上传到服务器

主要的逻辑是把MP3文件转成nsdata,然后通过base64string 转成字符串传给后台

NSString *url=[[HJInterfaceManager sharedInstance]addRichInfo];

NSMutableDictionary *mdic=[[NSMutableDictionary alloc]initWithCapacity:0];

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *mp3FilePath = [urlStr stringByAppendingPathComponent:mp3FilePathName]; ;//

//    NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"];

NSData *filedata=[NSData dataWithContentsOfFile:mp3FilePath];

SelfDataModel *selfModel=[SelfDataModel returnModelBySelectFMDB];

//        * @param familyId

//        * @param userId

//        * @param content

//        * @param richContent

//        * @param type

//        * @param sendTime

NSDate *date=[NSDate date];

NSDateFormatter *form=[[NSDateFormatter alloc]init];

[form setDateFormat:@"YYYY-MM-dd HH:mm:ss"];

NSString *datestr=[form stringFromDate:date ];

if (!self.isMyself) {

[mdic setObject:self.family.idNum forKey:@"familyId"];

}

[mdic setObject:selfModel.idNum forKey:@"userId"];

[mdic setObject:@"" forKey:@"content"];

[mdic setObject:[NSString stringWithFormat:@"%d",self.duration] forKey:@"duration"];

[mdic setObject:[filedata base64Encoding] forKey:@"richContents"];

[mdic setObject:@"SOUND" forKey:@"type"];

[mdic setObject:datestr forKey:@"sendTime"];

[HJHttpManager PostRequestWithUrl:url param:mdic finish:^(NSData *data) {

NSDictionary *dic=(NSDictionary *)data;

if ([dic[@"status"] isEqualToString:@"S"]) {

[MBProgressHUD showSuccess:@"发送成功"];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[self.navigationController popViewControllerAnimated:YES];

});

}else{

[MBProgressHUD showError:dic[@"message"]];

}

} failed:^(NSError *error) {

NSLog(@"请求失败");

}];


小帖士:

以下是监听声音分贝的方法

#pragma mark----监听声音分贝

/* 该方法确实会随环境音量变化而变化,但具体分贝值是否准确暂时没有研究 */

- (void)recordTimerChange {

//    [self.recorder updateMeters];//刷新音量数据

//    double lowPassResults = pow(10, (0.05 * [self.recorder peakPowerForChannel:0]));

//    NSLog(@"大小:%lf",lowPassResults);

self.recordview.timeLab.text=[NSString stringWithFormat:@"%d s",self.index];

self.index++;

[self.recorder updateMeters];

float  level;                // The linear 0.0 .. 1.0 value we need.

float  minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room.

float  decibels    = [self.recorder averagePowerForChannel:0];

if (decibels < minDecibels)

{

level = 0.0f;

}

else if (decibels >= 0.0f)

{

level = 1.0f;

}

else

{

float  root            = 2.0f;

float  minAmp          = powf(10.0f, 0.05f * minDecibels);

float  inverseAmpRange = 1.0f / (1.0f - minAmp);

float  amp            = powf(10.0f, 0.05f * decibels);

float  adjAmp          = (amp - minAmp) * inverseAmpRange;

level = powf(adjAmp, 1.0f / root);

}

float ben=level*120;

if (ben<20) {

[self.recordview.imageview setImage:[UIImage imageNamed:@"order_voice_1"]];

}

else if (ben<35) {

[self.recordview.imageview setImage:[UIImage imageNamed:@"order_voice_2"]];

}

else{

[self.recordview.imageview setImage:[UIImage imageNamed:@"order_voice_3"]];

}

NSLog(@"大小%lf",ben);

}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS 录音,播放并上传 的相关文章

  • 如何通过情节提要向导航栏添加后退按钮?

    我现在很困惑 我在网上到处都看到添加自定义后退按钮的教程 但我什至似乎无法启动默认按钮 在我的 MainViewController 中 我有performSegueWithIdentifier 然后在另一端 我希望导航栏在左侧有后退按钮
  • 我无法让 HealthKit 工作。缺失的权利

    我很难让 HealthKit 为我的 iOS 应用程序工作 我已经完成了所有步骤到目前为止我已经找到了 http jademind com blog posts healthkit api tutorial 似乎没有一个能解决我的问题 当我
  • 枚举 NSString 的最佳方法

    我正在寻找枚举 objc 对象 例如 NSString 的方法 我记得 Xcode4 版本中有一个新功能 它提供了一种新的 enum 方法 但不清楚 有人知道吗 好吧 我自己回答 我想我犯了一个错误 这就是我上面提到的新功能 typedef
  • 关于 S3 文件传输的权限

    我正在使用 S3TransferManager Sample 进行测试 我创建了Cognito并设置了IAM并最后更改了constants swift文件 我上传没有问题 但下载失败 错误信息是 下载失败 错误域 com amazonaws
  • 在 Swift 2.0 中隐藏 Home 指示器

    我希望隐藏 Home Indicator 虽然这在 Swift 中很简单 但在 SwiftUI 中似乎并不那么容易 我尝试使用这个 如何使用 SwiftUI 隐藏 home 指示器 https stackoverflow com quest
  • 在React-native中,如何更改NavigatorIOS的样式

    在react native中 如何更改NavigatorIOS的样式 例如背景颜色 谢谢你 var speedNews React createClass render function return
  • VNFaceObservation BoundingBox 在纵向模式下不缩放

    作为参考 这源于一个问题视觉API 我正在努力使用Vision通过a检测图像中的人脸VNDetectFaceRectanglesRequest 它在确定图像中正确的人脸数量并提供boundingBox对于每张脸 我的麻烦是由于我UIImag
  • UITableView 自动调整行大小约束在 iPhone 6Plus 上神秘破坏

    我有一个自定义的 UITableViewCell 它有一个缩略图和一堆文本 行高配置为使用自动计算 tableView estimatedRowHeight 129 tableView rowHeight UITableViewAutoma
  • 以编程方式显示 UIView

    我试图 通过动画 显示 UIView 具体来说 我想显示视图的中心部分 然后慢慢地显示它的外边缘 有点像拉开窗帘 我的第一次尝试是简单地将边界矩形设置得更小 并将其动画化为视图框架的完整大小 但这没有达到预期的效果 因为通过更改边界 我也更
  • SwiftUI TabbedView 仅显示第一个选项卡的内容

    我正在尝试建立一个TabbedView使用以下简单代码 TabbedView Text Hello world tabItemLabel Text Hello Text Foo bar tabItemLabel Text Foo 运行时 两
  • 如何使用 Unicode 十六进制值 (UTF-16) 在 Swift 中表达字符串

    我想在 Swift 中使用十六进制值编写 Unicode 字符串 我已阅读文档 https developer apple com library prerelease ios documentation Swift Conceptual
  • 在哪里可以找到有关 IOS 日历同步的优秀教程? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在开发 iPhone 应用程序 如何将新事件与 iOS 日历同步 您可以浏览此 github 代码
  • 当应用程序名称在 InfoPlist.strings 中本地化时,不同的应用程序名称取决于配置

    我们为每个配置使用具有不同 plist 的设置 像这样 目标 Info Dev plist 目标 Info Beta plist 这样我们的配置就可以拥有自己的 CFBundleDisplayName 并且我们可以通过设备上的应用程序名称来
  • 移动文件并覆盖[重复]

    这个问题在这里已经有答案了 即使同名文件已存在 我也尝试移动文件 NSFileManager moveItemAtURL location1 toURL location2 Does NSFileManager的方法moveItemAtUR
  • 当前位置在 Google 地图中不起作用

    我在 swift 3 中集成了谷歌地图 当地图屏幕出现而不显示当前位置时 我在 plist 文件中添加了两个键 并设置了 CLLocationManager delegate 和 requestAlwaysAuthorization cla
  • 如何在 iOS 中通过 3G 连接创建无线热点

    如果我愿意 我将如何创建一个像这样的应用程序MyWi http intelliborn com mywi html 即 WiFi 网络共享应用程序 实现该功能需要哪些步骤 我需要使用哪些框架 库 我们的目标不是尝试将此应用程序放入应用程序商
  • UINavigationBar setBackgroundImage 在 AppDelegate 中使用 Swift

    我正在尝试在 Swift 的 App Delegate 中全局设置导航栏的背景图像 我可以让它在单独的视图控制器上工作 如下所示 var topBar UINavigationBar topBar setBackgroundImage UI
  • UIWebView stringByEvaluatingJavaScriptFromString 在后台

    在 iOS 应用程序中 我正在运行一个相当大的脚本UIWebView using stringByEvaluatingJavaScriptFromString 就 JavaScript 字符串的长度而言较大 调用 JavaScript 后会
  • 关闭应用程序后如何调试

    我正在尝试重现问题 这需要在特定位置关闭并重新打开我的应用程序 这是我的问题 1 如何查看我的日志 使用NSLog命令 当我的 iPhone 未连接到 XCode 时 2 是否可以将iPhone模拟器的特定位置 例如市中心 设置为默认位置
  • Phonegap - cordova 在 Android 和 iOS 设备上延迟且缓慢

    我刚刚开始使用 zend studio 开始我的第一个 PhoneGap 项目 但是 在我构建并部署它之后 该应用程序非常慢 Android 和 iOS 均可 滚动滞后 如果我按下按钮 转到下一页的速度很慢 有什么办法可以提高它的性能吗 提

随机推荐