PCM文件转换为MP3文件

简要描述

主要协助云上越秀App将PCM音频文件转码成MP3文件功能代码示例;

主要内容

PCM录制示例代码:

引入第三方库:

  1. pod 'mp3lame-for-ios'

代码片段:

  1. - (void)startSpeechRecognizerWithFileName:(NSString *)fileName {
  2. [self.iFlySpeechRecognizer cancel];
  3. self.jsons = @[].mutableCopy;
  4. if ([PHAIUtils isNilOrEmpty:fileName]) {
  5. self.fileName = [PHAIFileManager generateFileName];
  6. } else {
  7. self.fileName = fileName;
  8. }
  9. NSString *pcmFileName = [NSString stringWithFormat:@"%@.pcm", self.fileName];
  10. //asr_audio_path 是录音文件名,设置value为nil或者为空取消保存,默认保存目录在Library/cache下。
  11. [_iFlySpeechRecognizer setParameter:pcmFileName forKey:[IFlySpeechConstant ASR_AUDIO_PATH]];
  12. //启动识别服务
  13. [self.iFlySpeechRecognizer startListening];
  14. }
  15. - (void)onCompleted:(IFlySpeechError *)error {
  16. IFlyLog(@"%s",__func__);
  17. self.result = nil;
  18. if (error.errorCode != 0) {
  19. IFlyLog(@"语音识别出现问题:%@", error.errorDesc);
  20. } else {
  21. PHAIFileManager *fileManager = PHAIFileManager.sharedManager;
  22. NSString *fileName = self.fileName;
  23. NSString *pcmFilePath = [fileManager getCachesPCMURLWithName:fileName].path;
  24. NSString *mp3FilePath = [fileManager getIATAudioFileWithName:fileName].path;
  25. int sampleRate = [PHAIIATConfig.sharedInstance.sampleRate intValue];
  26. __weak typeof(self) weakSelf = self;
  27. [PHAIMP3Encoder conventToMp3WithCafFilePath:pcmFilePath
  28. mp3FilePath:mp3FilePath
  29. sampleRate:sampleRate
  30. callback:^(BOOL complete) {
  31. if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(mp3EnconderComplete:fileName:)]) {
  32. [weakSelf.delegate mp3EnconderComplete:complete fileName:fileName];
  33. }
  34. AudioEncoderLog(@"转码完成");
  35. }];
  36. }
  37. if (self.delegate && [self.delegate respondsToSelector:@selector(recongnizingComplete:)]) {
  38. [self.delegate recongnizingComplete:error.errorCode == 0];
  39. }
  40. }

PCM转换成MP3的示例代码:

PHAIMP3Encoder.h

  1. #import <Foundation/Foundation.h>
  2. NS_ASSUME_NONNULL_BEGIN
  3. @interface PHAIMP3Encoder : NSObject
  4. + (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath
  5. mp3FilePath:(NSString *)mp3FilePath
  6. callback:(void(^)(BOOL complete))callback;
  7. + (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath
  8. mp3FilePath:(NSString *)mp3FilePath
  9. sampleRate:(int)sampleRate
  10. callback:(void(^)(BOOL complete))callback;
  11. @end
  12. NS_ASSUME_NONNULL_END

PHAIMP3Encoder.m

  1. #import "PHAIMP3Encoder.h"
  2. #import "lame/lame.h"
  3. #import "PHAILogFunctions.h"
  4. @implementation PHAIMP3Encoder
  5. + (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath
  6. mp3FilePath:(NSString *)mp3FilePath
  7. callback:(void (^)(BOOL))callback {
  8. [self conventToMp3WithCafFilePath:cafFilePath
  9. mp3FilePath:mp3FilePath
  10. sampleRate:16000
  11. callback:^(BOOL complete) {
  12. callback(complete);
  13. }];
  14. }
  15. + (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath
  16. mp3FilePath:(NSString *)mp3FilePath
  17. sampleRate:(int)sampleRate
  18. callback:(void(^)(BOOL complete))callback {
  19. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  20. @try {
  21. int read, write;
  22. FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb"); //source 被转换的音频文件位置
  23. if (pcm == NULL) {
  24. if ([NSFileManager.defaultManager fileExistsAtPath:cafFilePath]) {
  25. pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb");
  26. } else {
  27. AudioEncoderLog(@"文件无法打开: %@ %d", cafFilePath, [NSFileManager.defaultManager fileExistsAtPath:cafFilePath]);
  28. callback(NO);
  29. return;
  30. }
  31. }
  32. fseek(pcm, 4*1024, SEEK_CUR); //skip file header
  33. FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb+"); //output 输出生成的Mp3文件位置
  34. const int PCM_SIZE = sampleRate / 20 + 7200;
  35. const int MP3_SIZE = PCM_SIZE;
  36. short int pcm_buffer[PCM_SIZE*2];
  37. unsigned char mp3_buffer[MP3_SIZE];
  38. lame_t lame = lame_init();
  39. lame_set_in_samplerate(lame, sampleRate);
  40. lame_set_num_channels(lame,1);//设置1为单通道,默认为2双通道
  41. lame_set_VBR(lame, vbr_default);
  42. lame_init_params(lame);
  43. do {
  44. read = (int)fread(pcm_buffer, sizeof(short int), PCM_SIZE, pcm);
  45. if (read == 0) {
  46. write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
  47. } else {
  48. write = lame_encode_buffer(lame, pcm_buffer, NULL, read, mp3_buffer, MP3_SIZE);
  49. }
  50. fwrite(mp3_buffer, sizeof(char), write, mp3);
  51. } while (read != 0);
  52. lame_mp3_tags_fid(lame, mp3);
  53. lame_close(lame);
  54. fclose(mp3);
  55. fclose(pcm);
  56. } @catch (NSException *exception) {
  57. AudioEncoderLog(@"%@",[exception description]);
  58. if (callback) {
  59. callback(NO);
  60. }
  61. }
  62. @finally {
  63. AudioEncoderLog(@"-----\n MP3生成成功: %@ ----- \n", mp3FilePath);
  64. if (callback) {
  65. callback(YES);
  66. }
  67. }
  68. });
  69. }
  70. @end