04.iOS 使用lame将wav转换为mp3

在iOS设备中进行录音,录音文件的格式为wav。但这种格式文件会很大,上传到服务器会消耗大量流量。为了适应终端的播放功能以及文件大小的要求,特将wav转换为mp3格式文件来使用。

注意:
在录制wav文件时,需要使用双通道,否则在转换为MP3格式时,声音不对。

首先,下载lame源码:http://sourceforge.net/projects/lame/files/lame/3.99/,怎么编译可以查看这篇文章编译完成后,将fat-lame文件夹下的lame.hlibmp3lame.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) {
}];



未完待续。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值