/************************************************************ * * Hyphenate CONFIDENTIAL * __________________ * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * NOTICE: All information contained herein is, and remains * the property of Hyphenate Inc. * Dissemination of this information or reproduction of this material * is strictly forbidden unless prior written permission is obtained * from Hyphenate Inc. */ #import "EMCDDeviceManager+Media.h" #import "EMAudioPlayerUtil.h" #import "EMAudioRecorderUtil.h" #import "EMVoiceConverter.h" #import "DemoErrorCode.h" #import "EaseLocalDefine.h" typedef NS_ENUM(NSInteger, EMAudioSession){ EM_DEFAULT = 0, EM_AUDIOPLAYER, EM_AUDIORECORDER }; @implementation EMCDDeviceManager (Media) #pragma mark - AudioPlayer + (NSString*)dataPath { NSString *dataPath = [NSString stringWithFormat:@"%@/Library/appdata/chatbuffer", NSHomeDirectory()]; NSFileManager *fm = [NSFileManager defaultManager]; if(![fm fileExistsAtPath:dataPath]){ [fm createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:nil]; } return dataPath; } // Play the audio - (void)asyncPlayingWithPath:(NSString *)aFilePath completion:(void(^)(NSError *error))completon{ BOOL isNeedSetActive = YES; // Cancel if it is currently playing if([EMAudioPlayerUtil isPlaying]){ [EMAudioPlayerUtil stopCurrentPlaying]; isNeedSetActive = NO; } if (isNeedSetActive) { [self setupAudioSessionCategory:EM_AUDIOPLAYER isActive:YES]; } NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *wavFilePath = [[aFilePath stringByDeletingPathExtension] stringByAppendingPathExtension:@"wav"]; if ([EMVoiceConverter isMP3File:aFilePath]) { wavFilePath = aFilePath; } else { if (![fileManager fileExistsAtPath:wavFilePath]) { BOOL covertRet = [self convertAMR:aFilePath toWAV:wavFilePath]; if (!covertRet) { if (completon) { completon([NSError errorWithDomain:NSEaseLocalizedString(@"error.initRecorderFail", @"File format conversion failed") code:EMErrorFileTypeConvertionFailure userInfo:nil]); } return ; } } } [EMAudioPlayerUtil asyncPlayingWithPath:wavFilePath completion:^(NSError *error) { [self setupAudioSessionCategory:EM_DEFAULT isActive:NO]; if (completon) { completon(error); } }]; } - (void)stopPlaying{ [EMAudioPlayerUtil stopCurrentPlaying]; [self setupAudioSessionCategory:EM_DEFAULT isActive:NO]; } - (void)stopPlayingWithChangeCategory:(BOOL)isChange{ [EMAudioPlayerUtil stopCurrentPlaying]; if (isChange) { [self setupAudioSessionCategory:EM_DEFAULT isActive:NO]; } } - (BOOL)isPlaying{ return [EMAudioPlayerUtil isPlaying]; } #pragma mark - Recorder +(NSTimeInterval)recordMinDuration{ return 1.0; } // Start recording - (void)asyncStartRecordingWithFileName:(NSString *)fileName completion:(void(^)(NSError *error))completion{ NSError *error = nil; if ([self isRecording]) { if (completion) { error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.recordStoping", @"Record voice is not over yet") code:EMErrorAudioRecordStoping userInfo:nil]; completion(error); } return ; } if (!fileName || [fileName length] == 0) { error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.notFound", @"File path not exist") code:-1 userInfo:nil]; completion(error); return ; } BOOL isNeedSetActive = YES; if ([self isRecording]) { [EMAudioRecorderUtil cancelCurrentRecording]; isNeedSetActive = NO; } [self setupAudioSessionCategory:EM_AUDIORECORDER isActive:YES]; _recorderStartDate = [NSDate date]; NSString *recordPath = [NSString stringWithFormat:@"%@/%@", [EMCDDeviceManager dataPath], fileName]; [EMAudioRecorderUtil asyncStartRecordingWithPreparePath:recordPath completion:completion]; } // Stop recording -(void)asyncStopRecordingWithCompletion:(void(^)(NSString *recordPath, NSInteger aDuration, NSError *error))completion{ NSError *error = nil; if(![self isRecording]){ if (completion) { error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.recordNotBegin", @"Recording has not yet begun") code:EMErrorAudioRecordNotStarted userInfo:nil]; completion(nil,0,error); } return; } __weak typeof(self) weakSelf = self; _recorderEndDate = [NSDate date]; if([_recorderEndDate timeIntervalSinceDate:_recorderStartDate] < [EMCDDeviceManager recordMinDuration]){ if (completion) { error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.recordTooShort", @"Recording time is too short") code:EMErrorAudioRecordDurationTooShort userInfo:nil]; completion(nil,0,error); } // If the recording time is too shorty,in purpose delay one second dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)([EMCDDeviceManager recordMinDuration] * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [EMAudioRecorderUtil asyncStopRecordingWithCompletion:^(NSString *recordPath) { [weakSelf setupAudioSessionCategory:EM_DEFAULT isActive:NO]; }]; }); return ; } [EMAudioRecorderUtil asyncStopRecordingWithCompletion:^(NSString *recordPath) { if (completion) { if (recordPath) { // Convert wav to amr NSString *amrFilePath = [[recordPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"amr"]; BOOL convertResult = [self convertWAV:recordPath toAMR:amrFilePath]; NSError *error = nil; if (convertResult) { // Remove the wav NSFileManager *fm = [NSFileManager defaultManager]; [fm removeItemAtPath:recordPath error:nil]; } else { error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.initRecorderFail", @"File format conversion failed") code:EMErrorFileTypeConvertionFailure userInfo:nil]; } completion(amrFilePath,(int)[self->_recorderEndDate timeIntervalSinceDate:self->_recorderStartDate],error); } [weakSelf setupAudioSessionCategory:EM_DEFAULT isActive:NO]; } }]; } // Cancel recording -(void)cancelCurrentRecording{ [EMAudioRecorderUtil cancelCurrentRecording]; [self setupAudioSessionCategory:EM_DEFAULT isActive:NO]; } -(BOOL)isRecording{ return [EMAudioRecorderUtil isRecording]; } #pragma mark - Private -(NSError *)setupAudioSessionCategory:(EMAudioSession)session isActive:(BOOL)isActive{ BOOL isNeedActive = NO; if (isActive != _currActive) { isNeedActive = YES; _currActive = isActive; } NSError *error = nil; NSString *audioSessionCategory = nil; switch (session) { case EM_AUDIOPLAYER: audioSessionCategory = AVAudioSessionCategoryPlayback; break; case EM_AUDIORECORDER: audioSessionCategory = AVAudioSessionCategoryRecord; break; default: audioSessionCategory = AVAudioSessionCategoryAmbient; break; } AVAudioSession *audioSession = [AVAudioSession sharedInstance]; if (![_currCategory isEqualToString:audioSessionCategory]) { [audioSession setCategory:audioSessionCategory error:nil]; } if (isNeedActive) { BOOL success = [audioSession setActive:isActive withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]; if(!success || error){ error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.initPlayerFail", @"Failed to initialize AVAudioPlayer") code:-1 userInfo:nil]; return error; } } _currCategory = audioSessionCategory; return error; } #pragma mark - Convert - (BOOL)convertAMR:(NSString *)amrFilePath toWAV:(NSString *)wavFilePath { BOOL ret = NO; BOOL isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:amrFilePath]; if (isFileExists) { [EMVoiceConverter amrToWav:amrFilePath wavSavePath:wavFilePath]; isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:wavFilePath]; if (isFileExists) { ret = YES; } } return ret; } - (BOOL)convertWAV:(NSString *)wavFilePath toAMR:(NSString *)amrFilePath { BOOL ret = NO; BOOL isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:wavFilePath]; if (isFileExists) { [EMVoiceConverter wavToAmr:wavFilePath amrSavePath:amrFilePath]; isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:amrFilePath]; if (!isFileExists) { } else { ret = YES; } } return ret; } @end