您的位置:首页 > 移动开发 > IOS开发

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,添加按钮的代码如下:

// 说话按钮
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) {
}];


未完待续。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: