美文网首页
录音&播放笔记

录音&播放笔记

作者: xgou | 来源:发表于2016-08-15 17:53 被阅读227次

本文需要引入lame.h,以及第三方资源EMVoiceConverter

定义协议规则

@protocol PlayAVAudioManagerDelegate

- (void)playerBeiginLoadVoice;
- (void)playerBeiginPlay;
- (void)playerDidFinishPlay;
@end

@protocol RecordAVAudioDelegate

- (void)recordFail;
- (void)recordBeginConvert;
- (void)recordFinishedWithFilePath:(NSString *)filePath duration:(double)duration;
- (void)recordVoiceWithAvergePower:(float)average peakPower:(float)peak;
@end

自定义对象


@interface AVAudioManager : NSObject

@property (nonatomic, assign)id<PlayAVAudioManagerDelegate> playDelegate;

@property (nonatomic, assign)id<RecordAVAudioDelegate> recordDelegate;

+ (AVAudioManager *)sharedInstance;

//play

- (void)playVoiceWithUrl:(NSURL *)voiceUrl;

- (void)playVoiceWithData:(NSData *)voiceData;

- (void)stopPlay;

//record

- (void)startRecord;

- (void)stopRecord;

- (void)cancelRecord;

@end



static const int kCallBackInterval = 1;

@interface AVAudioManager ()

@property (nonatomic, strong) AVAudioSession *session;

@property (nonatomic, strong) AVAudioRecorder *recorder;

@property (nonatomic ,strong) AVAudioPlayer *player;

@property (nonatomic, strong) NSString *path;

@property (nonatomic, strong, readonly) NSString *cafPath;

@property (nonatomic, strong) NSString *mp3Path;

@property (nonatomic, strong) NSTimer *recordCallBackTimer;

@end

实现如下


@implementation AVAudioManager

+ (AVAudioManager *)sharedInstance {

    static AVAudioManager *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;

}

- (NSTimer *)recordCallBackTimer {

    if(_recordCallBackTimer == nil) {

        _recordCallBackTimer = [NSTimer scheduledTimerWithTimeInterval:kCallBackInterval target:self selector:@selector(recordCallBackAction) userInfo:nil repeats:YES];
    
    }
    return _recordCallBackTimer;

}

- (void)recordCallBackAction {

    if(!self.recorder.isRecording) {
        return;
    }
    
    [self.recorder updateMeters];
    
    if([self.recordDelegate respondsToSelector:@selector(recordVoiceWithAvergePower:peakPower:)]) {
    
        float peak  = [self.recorder peakPowerForChannel:0],
        
        averge = [self.recorder averagePowerForChannel:0];
        
        [self.recordDelegate recordVoiceWithAvergePower:averge peakPower:peak];
    
    }

}

- (AVAudioRecorder *)recorder {

    if(_recorder == nil) {
    
        NSError *recorderSetupError = nil;
        
        NSURL *url = [NSURL fileURLWithPath:self.cafPath];
        
        NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];
        
        //录音格式 无法使用
        
        [settings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
        
        //采样率,注意这里的采样率不要随意调整,太高会导致录音失真。
        
        [settings setValue :[NSNumber numberWithFloat:44100.0f] forKey: AVSampleRateKey];//44100.0
        
        //通道数
        
        [settings setValue :[NSNumber numberWithInt:2] forKey: AVNumberOfChannelsKey];
        
        //音频质量,采样质量
        
        [settings setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];
        
        _recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&recorderSetupError];
        
        if(recorderSetupError) {
            NSLog(@"%@",recorderSetupError);
        }
        
        _recorder.delegate = self;
    
    }
    
    return _recorder;

}

- (AVAudioSession *)session {

    if(_session == nil) {
    
        _session = [AVAudioSession sharedInstance];
        
        NSError *sessionError;
        
        [_session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
        
        [_session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
        
        [_session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
        
        if(_session == nil) {
        
            NSLog(@"Error creating session: %@", [sessionError description]);
        
        }
    
    }
    
    return _session;

}

- (NSString *)cafPath {

    return [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.caf"];

}

- (NSString *)mp3Path {

    return _mp3Path ? : (_mp3Path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp3",@([NSDate timeIntervalSinceReferenceDate])]]);

}

- (void)playVoiceWithUrl:(NSURL *)voiceUrl {

    dispatch_async(dispatch_queue_create("playSoundFromUrl", NULL),^{
    
        [self.playDelegate playerBeiginLoadVoice];
        
        NSData *data = nil;
        
        if(voiceUrl.isFileURL) {
            //播放的时候需要转化下格式。
            if(![EMVoiceConverter isAMRFile:voiceUrl.absoluteString]) {
            
                data = [NSData dataWithContentsOfURL:voiceUrl];
            
            }
            else {
            
                NSString *wavFilePath = [[voiceUrl.absoluteString stringByDeletingPathExtension] stringByAppendingPathExtension:@"wav"];
            
                if(![[NSFileManager defaultManager] fileExistsAtPath:voiceUrl.absoluteString]) {
                
                    [EMVoiceConverter amrToWav:voiceUrl.absoluteString wavSavePath:wavFilePath];
                
                    data = [NSData dataWithContentsOfFile:wavFilePath];
                
                }
            
            }
        
        }
        
        else {
        
            data = [NSData dataWithContentsOfURL:voiceUrl];
        
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
        
            [self playVoiceWithData:data];
        
        });
    
    });

}

//播放逻辑

- (void)playVoiceWithData:(NSData *)voiceData {

    [self setupPlaySound];

    if (_player) {
    
        [_player stop];
        
        _player.delegate = nil;
        
        _player = nil;
    
    }
    
    NSError *playerError;
    
    _player = [[AVAudioPlayer alloc] initWithData:voiceData error:&playerError];
    
    _player.volume = 1.0f;
    
    if(_player == nil) {
    
        NSLog(@"ERror creating player: %@", [playerError description]);
    
        return;
    
    }
    
    _player.delegate = self;
    [_player play];
    [self.playDelegate playerBeiginPlay];
}

- (void)stopPlay {
    if (self.player.isPlaying) {
        [self.player stop];
    }
}

-(void)setupPlaySound {

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];

}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    [self.playDelegate playerDidFinishPlay];
}

- (void)applicationWillResignActive:(UIApplication *)application {
    [self.playDelegate playerDidFinishPlay];
}

//录音逻辑

- (void)startRecord {

    self.mp3Path = nil;
    
    self.session = nil;
// 判断权限问题
    if ([self.session respondsToSelector:@selector(requestRecordPermission:)]) {
    
        [self.session performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
            if (!granted) {
                NSLog(@"disable");
                return;
            }
        }];
    
    }
    
    [self.session setActive:YES error:nil];
    
    self.recorder.meteringEnabled = YES;
    
    [self.recorder prepareToRecord];
    
    [self.recorder record];
    
    [self.recordCallBackTimer fire];

}

- (void)stopRecord {

    double cTime = self.recorder.currentTime;
    
    [self.recorder stop];
    
    [self closeTimerRecord];
    
    if (cTime > 1) {
    
        [self audio_PCMtoMP3WithDuration:cTime];
    
    }else {
    
        [_recorder deleteRecording];
    
        if ([self.recordDelegate respondsToSelector:@selector(recordFail)]) {
        
            [self.recordDelegate recordFail];
        
        }
    
    }

}

- (void)cancelRecord {

    [self.recorder stop];
    
    [self.recorder deleteRecording];
    
    [self closeTimerRecord];

}

- (void)closeTimerRecord {

    [self.recordCallBackTimer invalidate];
    
    self.recordCallBackTimer = nil;

}


#pragma mark - Convert Utils

//格式转换,兼容Android
- (void)audio_PCMtoMP3WithDuration:(double)timer {

    NSString *cafFilePath = self.cafPath;
    
    NSString *mp3FilePath = self.mp3Path;
    
    // remove the old mp3 file
    
    [self deleteMp3Cache];
    
    NSLog(@"MP3转换开始");
    
    if ([self.recordDelegate respondsToSelector:@selector(recordBeginConvert)]) {
    
        [self.recordDelegate recordBeginConvert];
    
    }
    
    @try {
    
        size_t read;
        
        int write;
        
        FILE *pcm = fopen([cafFilePath 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, (int)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 {
    
        [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategorySoloAmbient error: nil];
    
    }
    
    [self deleteCafCache];
    
    NSLog(@"MP3转换结束");
    
    if ([self.recordDelegate respondsToSelector:@selector(recordFinishedWithFilePath:duration:)]) {
    
    [self.recordDelegate recordFinishedWithFilePath:self.mp3Path duration:timer];
    
    }

}

- (void)deleteMp3Cache {
    [self deleteFileWithPath:self.mp3Path];
}

- (void)deleteCafCache {
    [self deleteFileWithPath:self.cafPath];
}

- (void)deleteFileWithPath:(NSString *)path {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager removeItemAtPath:path error:nil];

}

相关文章

网友评论

      本文标题:录音&播放笔记

      本文链接:https://www.haomeiwen.com/subject/qniusttx.html