04.iOS 使用lame将wav转换为mp3
2015-12-10 17:35
579 查看
在iOS设备中进行录音,录音文件的格式为wav。但这种格式文件会很大,上传到服务器会消耗大量流量。为了适应终端的播放功能以及文件大小的要求,特将wav转换为mp3格式文件来使用。
注意:在录制wav文件时,需要使用双通道,否则在转换为MP3格式时,声音不对。
首先,下载lame源码:http://sourceforge.net/projects/lame/files/lame/3.99/,怎么编译可以查看这篇文章。编译完成后,将fat-lame文件夹下的lame.h和libmp3lame.a文件导入项目中。如图:
我们打开物流唐山app,点击首页地图下的发布按钮,在弹出的录音界面中,当我们点击"按住说话"按钮
,将会开始录音。
录音说明:
1.当录音时间小于2.5s的时候,将会弹出说话时间太短对话框,录音将会取消。
2.当点击按钮,但是在按钮外部松开的时候,录音也将会取消。
3.只有当手指在按钮内部松开,并且录音时间大于2.5s的时候,录音有效,保存为wav格式文件。
所以我们需要添加三个Target,添加按钮的代码如下:
接下来就是使用AVAudioRecorder进行录音:
录音完成会调用- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag;我们可以在这个函数里边将录音文件转换为mp3文件
在上面两个函数中,都出现了VoiceConverter这个类,这是我自己定义的一个工具类,用于配置录音以及将wav转换为mp3。以下是其代码:
VoiceConverter.h
VoiceConverter.mm
说明:
由于在VoiceConverter.mm文件中使用了第三方静态库,所以VoiceConverter.mm后缀是mm,用于混合编译。
录音完成以后,会弹出提交录音对话框:
使用AFNetworking将录音文件提交到服务器即可。
未完待续。。。
注意:在录制wav文件时,需要使用双通道,否则在转换为MP3格式时,声音不对。
首先,下载lame源码:http://sourceforge.net/projects/lame/files/lame/3.99/,怎么编译可以查看这篇文章。编译完成后,将fat-lame文件夹下的lame.h和libmp3lame.a文件导入项目中。如图:
我们打开物流唐山app,点击首页地图下的发布按钮,在弹出的录音界面中,当我们点击"按住说话"按钮
,将会开始录音。
录音说明:
1.当录音时间小于2.5s的时候,将会弹出说话时间太短对话框,录音将会取消。
2.当点击按钮,但是在按钮外部松开的时候,录音也将会取消。
3.只有当手指在按钮内部松开,并且录音时间大于2.5s的时候,录音有效,保存为wav格式文件。
所以我们需要添加三个Target,添加按钮的代码如下:
// 说话按钮 UIImage *sayButtonImage = [UIImage imageNamed:@"完成_07"]; self.sayButton = [[UIButton alloc] initWithFrame:CGRectMake((ScreenW - sayButtonImage.size.width) / 2, ScreenH - 60 - sayButtonImage.size.height, sayButtonImage.size.width,sayButtonImage.size.height)]; [self.sayButton setImage:sayButtonImage forState:UIControlStateNormal]; [self.sayButton addTarget:self action:@selector(touchDown) forControlEvents:UIControlEventTouchDown]; [self.sayButton addTarget:self action:@selector(touchUpInside) forControlEvents:UIControlEventTouchUpInside]; [self.sayButton addTarget:self action:@selector(touchOutside) forControlEvents:UIControlEventTouchUpOutside]; [self addSubview:self.sayButton];
接下来就是使用AVAudioRecorder进行录音:
- (void)startRecorder { //根据当前时间生成文件名 NSString *recordFileName = [VoiceConverter getCurrentTimeString]; //获取路径 self.recordFilePath = [VoiceConverter getPathFromFileName:recordFileName ofType:@"wav"]; //初始化录音 self.recorder = [[AVAudioRecorder alloc] initWithURL:[NSURL fileURLWithPath:self.recordFilePath] settings:[VoiceConverter GetAudioRecorderSettingDict] error:nil]; self.recorder.delegate = self; //准备录音 if ([self.recorder prepareToRecord]){ [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryRecord error:nil]; [[AVAudioSession sharedInstance] setActive:YES error:nil]; [self.recorder record]; } }
录音完成会调用- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag;我们可以在这个函数里边将录音文件转换为mp3文件
#pragma mark - 录音结束返回的信息 - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag { NSFileManager *manager = [[NSFileManager alloc] init]; if (![manager fileExistsAtPath:self.recordFilePath]) { return;// 如果文件不存在自己返回 } // 获取转换后的mp3文件路径 NSString *mp3FilePath = [VoiceConverter getPathFromFileName:@"lvRecord1" ofType:@"mp3"]; if ([VoiceConverter ConvertWavToMp3:self.recordFilePath mp3SavePath:mp3FilePath]) { [manager removeItemAtPath:self.recordFilePath error:NULL];// 删除录音文件 } TSLPutAudioView *putView = [[TSLPutAudioView alloc] init]; putView.filePath = mp3FilePath; [putView show]; }
在上面两个函数中,都出现了VoiceConverter这个类,这是我自己定义的一个工具类,用于配置录音以及将wav转换为mp3。以下是其代码:
VoiceConverter.h
#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface VoiceConverter : NSObject /** * 转换wav到mp3 * * @param aWavPath wav文件路径 * @param aSavePath mp3保存路径 * * @return 0失败 1成功 */ + (int)ConvertWavToMp3:(NSString *)aWavPath mp3SavePath:(NSString *)aSavePath; /** 获取录音设置. 建议使用此设置,如有修改,则转换amr时也要对应修改参数,比较麻烦 @returns 录音设置 */ + (NSDictionary*)GetAudioRecorderSettingDict; /** * 获取缓存路径 * * @return 缓存路径 */ + (NSString *)getCacheDirectory; /** * 生成当前时间字符串 */ + (NSString *)getCurrentTimeString; /** * 通过名字及类型获得文件路径 * * @param fileName 文件名 * @param type 文件类型 * * @return 文件路径 */ + (NSString *)getPathFromFileName:(NSString *)fileName ofType:(NSString *)type; @end
VoiceConverter.mm
#import "VoiceConverter.h" #import "lame.h" @implementation VoiceConverter //获取录音设置 + (NSDictionary*)GetAudioRecorderSettingDict{ NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithFloat: 8000.0],AVSampleRateKey, //采样率 [NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey, [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16 [NSNumber numberWithInt: 2], AVNumberOfChannelsKey,//通道的数目 // [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,//大端还是小端 是内存的组织方式 // [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,//采样信号是整数还是浮点数 // [NSNumber numberWithInt: AVAudioQualityMedium],AVEncoderAudioQualityKey,//音频编码质量 nil]; return recordSetting; } + (int)ConvertWavToMp3:(NSString *)aWavPath mp3SavePath:(NSString *)aSavePath { int state = 0; @try { int read, write; FILE *pcm = fopen([aWavPath cStringUsingEncoding:NSASCIIStringEncoding], "rb"); //source fseek(pcm, 4*1024, SEEK_CUR); //skip file header FILE *mp3 = fopen([aSavePath cStringUsingEncoding:NSASCIIStringEncoding], "wb"); //output 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); // 双声道 lame_set_in_samplerate(lame, 8000); // 8k采样率 lame_set_brate(lame, 16); // 压缩的比特率为16 lame_set_quality(lame, 2); // mp3音质 lame_init_params(lame); do { read = (int)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); state = 1; } @catch (NSException *exception) { state = 0; } @finally { return state; } } #pragma mark - 通过名字及类型获得文件路径 /** * 通过名字及类型获得文件路径 * * @param fileName 文件名 * @param type 文件类型 * * @return 文件路径 */ + (NSString *)getPathFromFileName:(NSString *)fileName ofType:(NSString *)type { NSString *filePath = [[[self getCacheDirectory]stringByAppendingPathComponent:fileName]stringByAppendingPathExtension:type]; NSFileManager *filemanager = [[NSFileManager alloc]init]; if ([filemanager fileExistsAtPath:filePath]){ // 如果文件已存在,删除文件 [filemanager removeItemAtPath:filePath error:NULL]; } return filePath; } #pragma mark - 获得缓存路径 /** * 获取缓存路径 * * @return 缓存路径 */ + (NSString *)getCacheDirectory { NSString *cache = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString *voicePath = [cache stringByAppendingPathComponent:@"Voice"]; NSFileManager *filemanager = [[NSFileManager alloc]init]; if (![filemanager fileExistsAtPath:voicePath]){ [filemanager createDirectoryAtPath:voicePath withIntermediateDirectories:YES attributes:nil error:NULL]; } return voicePath; } #pragma mark - 生成当前时间字符串 + (NSString *)getCurrentTimeString{ NSDateFormatter *dateformat = [[NSDateFormatter alloc]init]; [dateformat setDateFormat:@"yyyyMMddHHmmss"]; return [dateformat stringFromDate:[NSDate date]]; } @end
说明:
+ (NSDictionary*)GetAudioRecorderSettingDict;//该函数返回录音设置参数 + (int)ConvertWavToMp3:(NSString *)aWavPath mp3SavePath:(NSString *)aSavePath;//该函数将wav文件转换为mp3文件
由于在VoiceConverter.mm文件中使用了第三方静态库,所以VoiceConverter.mm后缀是mm,用于混合编译。
录音完成以后,会弹出提交录音对话框:
使用AFNetworking将录音文件提交到服务器即可。
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:@"http://www.560315.com/MobileAPI/AudioAdd" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { [formData appendPartWithFileData:[NSDatadataWithContentsOfFile:self.filePath] name:@"upname" fileName:[self.filePathlastPathComponent] mimeType:@"audio/mp3"]; } success:^(AFHTTPRequestOperation *operation, id responseObject) { // 上传成功 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { }];
未完待续。。。
相关文章推荐
- iOS重新定义系统自带的方法,如 重定义 CGRectMake 和 CGPointMake 可以解决屏幕适配的问题
- IOS中的触摸事件
- iOS 十进制 二进制 转换
- 需求 - 12 - 图片缓存
- ios 学习笔记之控件属性
- 模型的应用 && Xib &&代理模式的应用【应用管理】
- ios 实现图片上传的2种方式
- 【iOS 开发】如何去除tableView多余的空Cell
- Teemo__Tip1__iOS界面跳转及刷新
- iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用
- iOS多线程编程之NSThread的使用
- iOS 学习日志 :利用AFNetWorking 做文件上传
- mask遮障效果实现注水动画
- iOS LocationManager定位国内偏移,火星坐标(GCJ-02)解决方法:接口转换法,代码转换法
- ios 学习总结之动画(转)
- iOS 学习日志:调用相机拍照获得图片旋转问题
- 处理iOS报错: Warning “attempt to present ViewController whose view is not in the window hierarchy”
- ios 中的autoresizingMask(转载)
- 使用IOS企业版证书发布应用
- iOS开发融云即时通讯集成详细步骤