录音(PCM格式)、播放、lame转码成MP3

林俊英
2023-12-01
引入下面两个库。lame是个第三方库。
#import <AVFoundation/AVFoundation.h>
#import "lame.framework/Headers/lame.h"

#pragma mark - 音频转码
/**
 * 
音频转码,由 PCM 转码为 MP3
 */

- (
void )convertToMP3
{
   
NSString * mp3FileName = @"sendRecord.mp3" ;
   
NSString * documentPath = [ NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) lastObject ];
   
NSString *mp3FilePath = [documentPath stringByAppendingPathComponent :mp3FileName];
   
NSString * audioFilePath = [ NSString stringWithFormat : @"%@/audio.caf" , documentPath];
   
   
NSLog ( @"audioPath:%@" , audioFilePath);
   
   
@try {
       
int read, write;
       
       
FILE *pcm = fopen ([audioFilePath cStringUsingEncoding : 1 ], "rb" );  //source 被转换的音频文件位置
       
fseek (pcm, 4 * 1024 , SEEK_CUR );                                   //skip file header
       
FILE *mp3 = fopen ([mp3FilePath cStringUsingEncoding : 1 ], "wb" );  //output 输出生成的 Mp3 文件位置
       
       
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_in_samplerate (lame, 11025.0 );
       
lame_set_VBR (lame, vbr_default );
       
lame_init_params (lame);
       
       
do {
            read =
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);
    }
   
@catch (NSException *exception) {
       
NSLog ( @"%@" ,[exception description ]);
    }
   
@finally
    {
       
NSLog ( @"MP3 生成成功 :%@" , mp3FilePath);
    }
}

#pragma mark - 取得录音文件保存路径
/**
 * 
取得录音原始文件保存路径
 *
 * 
@return 录音原始文件路径
 */

- (
NSURL *)getSavePath{
   
NSString *urlStr=[ NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) lastObject ];
    urlStr=[urlStr
stringByAppendingPathComponent : @"audio.caf" ];
   
NSURL *url=[ NSURL fileURLWithPath :urlStr];
   
return url;
}

- (
NSString *)getSavePathToString{
   
NSString *urlStr=[ NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) lastObject ];
    urlStr=[urlStr
stringByAppendingPathComponent : @"audio.caf" ];
   
return urlStr;
}

/**
 * 
取得 MP3 文件保存路径
 *
 * 
@return MP3 文件路径
 */

- (
NSURL *)getMP3Path
{
   
// 播放 MP3
   
NSString * mp3FileName = @"sendRecord.mp3" ;
   
NSString * documentPath = [ NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) lastObject ];
   
NSString *mp3FilePath = [documentPath stringByAppendingPathComponent :mp3FileName];
   
return [ NSURL fileURLWithPath :mp3FilePath];
}

- (
NSString *)getMP3PathToString
{
   
// 播放 MP3
   
NSString * mp3FileName = @"sendRecord.mp3" ;
   
NSString * documentPath = [ NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ) lastObject ];
   
NSString *mp3FilePath = [documentPath stringByAppendingPathComponent :mp3FileName];
   
return mp3FilePath;
}

#pragma mark - 取得录音文件设置
/**
 * 
取得录音文件设置
 *
 * 
@return 录音设置
 */

-(
NSDictionary *)getAudioSetting{
   
NSMutableDictionary *dicM=[ NSMutableDictionary dictionary ];
   
// 设置录音格式
    [dicM
setObject : @( kAudioFormatLinearPCM ) forKey : AVFormatIDKey ];
   
// 设置录音采样率, 8000 是电话采样率,对于一般录音已经够了
    [dicM
setObject : @(11025.0) forKey : AVSampleRateKey ];
   
// 设置通道 , 这里采用单声道
    [dicM
setObject : @(2) forKey : AVNumberOfChannelsKey ];
   
// 每个采样点位数 , 分为 8 16 24 32
    [dicM
setObject : @(16) forKey : AVLinearPCMBitDepthKey ];
   
// 是否使用浮点数采样
   
//    [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
   
// 音频编码质量
    [dicM
setObject : @( AVAudioQualityLow ) forKey : AVEncoderAudioQualityKey ];
   
//.... 其他设置等
   
return dicM;
}

#pragma mark - 懒加载
- ( AVAudioRecorder *)audioRecorder{
   
if ( _audioRecorder == nil ) {
       
// 录音相关
       
AVAudioSession *audioSession=[ AVAudioSession sharedInstance ];
       
// 设置为播放和录音状态,以便可以在录制完之后播放录音
        [audioSession
setCategory : AVAudioSessionCategoryPlayAndRecord error : nil ];
        [audioSession
setActive : YES error : nil ];
       
       
// 创建录音文件保存路径
       
NSURL * url = [ self getSavePath ];
       
// 创建录音格式设置
       
NSDictionary * setting = [ self getAudioSetting ];
       
// 创建录音机
       
NSError * error = nil ;
       
_audioRecorder = [[ AVAudioRecorder alloc ] initWithURL :url settings :setting error :&error];
       
       
_audioRecorder . delegate = self ;
       
if (error) {
           
NSLog ( @" 创建录音机对象时发生错误,错误信息: %@" , error. localizedDescription );
           
return nil ;
        }
    }
   
return _audioRecorder ;
}

/**
 * 
创建播放器
 *
 * 
@return 播放器
 */

- (
AVAudioPlayer *)audioPlayer{
   
if ( _audioPlayer == nil ) {
       
// 录音相关
       
AVAudioSession *audioSession=[ AVAudioSession sharedInstance ];
       
// 设置为播放和录音状态,以便可以在录制完之后播放录音
        [audioSession
setCategory : AVAudioSessionCategoryPlayback error : nil ];
        [audioSession
setActive : YES error : nil ];
       
NSURL * url = [ self getSavePath ];
       
       
NSError * error = nil ;
       
_audioPlayer = [[ AVAudioPlayer alloc ] initWithContentsOfURL :url error :&error];
       
_audioPlayer . volume = 1.0 ;
       
_audioPlayer . numberOfLoops = 0 ;
       
_audioPlayer . delegate = self ;
        [
_audioPlayer prepareToPlay ];
       
if (error) {
           
NSLog ( @" 创建播放器过程中发生错误,错误信息: %@" , error. localizedDescription );
           
return nil ;
        }
    }
   
return _audioPlayer ;
}
 类似资料: