04.iOS 使用lame將wav轉換為mp3
阿新 • • 發佈:2019-02-02
在iOS裝置中進行錄音,錄音檔案的格式為wav。但這種格式檔案會很大,上傳到伺服器會消耗大量流量。為了適應終端的播放功能以及檔案大小的要求,特將wav轉換為mp3格式檔案來使用。
注意:在錄製wav檔案時,需要使用雙通道,否則在轉換為MP3格式時,聲音不對。
VoiceConverter.mm
說明:
錄音完成以後,會彈出提交錄音對話方塊:
未完待續。。。
注意:在錄製wav檔案時,需要使用雙通道,否則在轉換為MP3格式時,聲音不對。
我們開啟物流唐山app,點選首頁地圖下的釋出按鈕,在彈出的錄音介面中,當我們點選"按住說話"按鈕 ,將會開始錄音。
錄音說明:
1.當錄音時間小於2.5s的時候,將會彈出說話時間太短對話方塊,錄音將會取消。
2.當點選按鈕,但是在按鈕外部鬆開的時候,錄音也將會取消。
3.只有當手指在按鈕內部鬆開,並且錄音時間大於2.5s的時候,錄音有效,儲存為wav格式檔案。
所以我們需要新增三個Target,新增按鈕的程式碼如下:
接下來就是使用AVAudioRecorder進行錄音:// 說話按鈕 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];
錄音完成會呼叫- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag;我們可以在這個函式裡邊將錄音檔案轉換為mp3檔案- (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]; } }
在上面兩個函式中,都出現了VoiceConverter這個類,這是我自己定義的一個工具類,用於配置錄音以及將wav轉換為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.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) {
}];
未完待續。。。