EMCDDeviceManager+Media.m 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /************************************************************
  2. * * Hyphenate CONFIDENTIAL
  3. * __________________
  4. * Copyright (C) 2016 Hyphenate Inc. All rights reserved.
  5. *
  6. * NOTICE: All information contained herein is, and remains
  7. * the property of Hyphenate Inc.
  8. * Dissemination of this information or reproduction of this material
  9. * is strictly forbidden unless prior written permission is obtained
  10. * from Hyphenate Inc.
  11. */
  12. #import "EMCDDeviceManager+Media.h"
  13. #import "EMAudioPlayerUtil.h"
  14. #import "EMAudioRecorderUtil.h"
  15. #import "EMVoiceConverter.h"
  16. #import "DemoErrorCode.h"
  17. #import "EaseLocalDefine.h"
  18. typedef NS_ENUM(NSInteger, EMAudioSession){
  19. EM_DEFAULT = 0,
  20. EM_AUDIOPLAYER,
  21. EM_AUDIORECORDER
  22. };
  23. @implementation EMCDDeviceManager (Media)
  24. #pragma mark - AudioPlayer
  25. + (NSString*)dataPath
  26. {
  27. NSString *dataPath = [NSString stringWithFormat:@"%@/Library/appdata/chatbuffer", NSHomeDirectory()];
  28. NSFileManager *fm = [NSFileManager defaultManager];
  29. if(![fm fileExistsAtPath:dataPath]){
  30. [fm createDirectoryAtPath:dataPath
  31. withIntermediateDirectories:YES
  32. attributes:nil
  33. error:nil];
  34. }
  35. return dataPath;
  36. }
  37. // Play the audio
  38. - (void)asyncPlayingWithPath:(NSString *)aFilePath
  39. completion:(void(^)(NSError *error))completon{
  40. BOOL isNeedSetActive = YES;
  41. // Cancel if it is currently playing
  42. if([EMAudioPlayerUtil isPlaying]){
  43. [EMAudioPlayerUtil stopCurrentPlaying];
  44. isNeedSetActive = NO;
  45. }
  46. if (isNeedSetActive) {
  47. [self setupAudioSessionCategory:EM_AUDIOPLAYER
  48. isActive:YES];
  49. }
  50. NSFileManager *fileManager = [NSFileManager defaultManager];
  51. NSString *wavFilePath = [[aFilePath stringByDeletingPathExtension] stringByAppendingPathExtension:@"wav"];
  52. if (![fileManager fileExistsAtPath:wavFilePath]) {
  53. BOOL covertRet = [self convertAMR:aFilePath toWAV:wavFilePath];
  54. if (!covertRet) {
  55. if (completon) {
  56. completon([NSError errorWithDomain:NSEaseLocalizedString(@"error.initRecorderFail", @"File format conversion failed")
  57. code:EMErrorFileTypeConvertionFailure
  58. userInfo:nil]);
  59. }
  60. return ;
  61. }
  62. }
  63. [EMAudioPlayerUtil asyncPlayingWithPath:wavFilePath
  64. completion:^(NSError *error)
  65. {
  66. [self setupAudioSessionCategory:EM_DEFAULT
  67. isActive:NO];
  68. if (completon) {
  69. completon(error);
  70. }
  71. }];
  72. }
  73. - (void)stopPlaying{
  74. [EMAudioPlayerUtil stopCurrentPlaying];
  75. [self setupAudioSessionCategory:EM_DEFAULT
  76. isActive:NO];
  77. }
  78. - (void)stopPlayingWithChangeCategory:(BOOL)isChange{
  79. [EMAudioPlayerUtil stopCurrentPlaying];
  80. if (isChange) {
  81. [self setupAudioSessionCategory:EM_DEFAULT
  82. isActive:NO];
  83. }
  84. }
  85. - (BOOL)isPlaying{
  86. return [EMAudioPlayerUtil isPlaying];
  87. }
  88. #pragma mark - Recorder
  89. +(NSTimeInterval)recordMinDuration{
  90. return 1.0;
  91. }
  92. // Start recording
  93. - (void)asyncStartRecordingWithFileName:(NSString *)fileName
  94. completion:(void(^)(NSError *error))completion{
  95. NSError *error = nil;
  96. if ([self isRecording]) {
  97. if (completion) {
  98. error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.recordStoping", @"Record voice is not over yet")
  99. code:EMErrorAudioRecordStoping
  100. userInfo:nil];
  101. completion(error);
  102. }
  103. return ;
  104. }
  105. if (!fileName || [fileName length] == 0) {
  106. error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.notFound", @"File path not exist")
  107. code:-1
  108. userInfo:nil];
  109. completion(error);
  110. return ;
  111. }
  112. BOOL isNeedSetActive = YES;
  113. if ([self isRecording]) {
  114. [EMAudioRecorderUtil cancelCurrentRecording];
  115. isNeedSetActive = NO;
  116. }
  117. [self setupAudioSessionCategory:EM_AUDIORECORDER
  118. isActive:YES];
  119. _recorderStartDate = [NSDate date];
  120. NSString *recordPath = [NSString stringWithFormat:@"%@/%@", [EMCDDeviceManager dataPath], fileName];
  121. [EMAudioRecorderUtil asyncStartRecordingWithPreparePath:recordPath
  122. completion:completion];
  123. }
  124. // Stop recording
  125. -(void)asyncStopRecordingWithCompletion:(void(^)(NSString *recordPath,
  126. NSInteger aDuration,
  127. NSError *error))completion{
  128. NSError *error = nil;
  129. if(![self isRecording]){
  130. if (completion) {
  131. error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.recordNotBegin", @"Recording has not yet begun")
  132. code:EMErrorAudioRecordNotStarted
  133. userInfo:nil];
  134. completion(nil,0,error);
  135. }
  136. return;
  137. }
  138. __weak typeof(self) weakSelf = self;
  139. _recorderEndDate = [NSDate date];
  140. if([_recorderEndDate timeIntervalSinceDate:_recorderStartDate] < [EMCDDeviceManager recordMinDuration]){
  141. if (completion) {
  142. error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.recordTooShort", @"Recording time is too short")
  143. code:EMErrorAudioRecordDurationTooShort
  144. userInfo:nil];
  145. completion(nil,0,error);
  146. }
  147. // If the recording time is too shorty,in purpose delay one second
  148. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)([EMCDDeviceManager recordMinDuration] * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  149. [EMAudioRecorderUtil asyncStopRecordingWithCompletion:^(NSString *recordPath) {
  150. [weakSelf setupAudioSessionCategory:EM_DEFAULT isActive:NO];
  151. }];
  152. });
  153. return ;
  154. }
  155. [EMAudioRecorderUtil asyncStopRecordingWithCompletion:^(NSString *recordPath) {
  156. if (completion) {
  157. if (recordPath) {
  158. // Convert wav to amr
  159. NSString *amrFilePath = [[recordPath stringByDeletingPathExtension]
  160. stringByAppendingPathExtension:@"amr"];
  161. BOOL convertResult = [self convertWAV:recordPath toAMR:amrFilePath];
  162. NSError *error = nil;
  163. if (convertResult) {
  164. // Remove the wav
  165. NSFileManager *fm = [NSFileManager defaultManager];
  166. [fm removeItemAtPath:recordPath error:nil];
  167. }
  168. else {
  169. error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.initRecorderFail", @"File format conversion failed")
  170. code:EMErrorFileTypeConvertionFailure
  171. userInfo:nil];
  172. }
  173. completion(amrFilePath,(int)[self->_recorderEndDate timeIntervalSinceDate:self->_recorderStartDate],error);
  174. }
  175. [weakSelf setupAudioSessionCategory:EM_DEFAULT isActive:NO];
  176. }
  177. }];
  178. }
  179. // Cancel recording
  180. -(void)cancelCurrentRecording{
  181. [EMAudioRecorderUtil cancelCurrentRecording];
  182. [self setupAudioSessionCategory:EM_DEFAULT isActive:NO];
  183. }
  184. -(BOOL)isRecording{
  185. return [EMAudioRecorderUtil isRecording];
  186. }
  187. #pragma mark - Private
  188. -(NSError *)setupAudioSessionCategory:(EMAudioSession)session
  189. isActive:(BOOL)isActive{
  190. BOOL isNeedActive = NO;
  191. if (isActive != _currActive) {
  192. isNeedActive = YES;
  193. _currActive = isActive;
  194. }
  195. NSError *error = nil;
  196. NSString *audioSessionCategory = nil;
  197. switch (session) {
  198. case EM_AUDIOPLAYER:
  199. audioSessionCategory = AVAudioSessionCategoryPlayback;
  200. break;
  201. case EM_AUDIORECORDER:
  202. audioSessionCategory = AVAudioSessionCategoryRecord;
  203. break;
  204. default:
  205. audioSessionCategory = AVAudioSessionCategoryAmbient;
  206. break;
  207. }
  208. AVAudioSession *audioSession = [AVAudioSession sharedInstance];
  209. if (![_currCategory isEqualToString:audioSessionCategory]) {
  210. [audioSession setCategory:audioSessionCategory error:nil];
  211. }
  212. if (isNeedActive) {
  213. BOOL success = [audioSession setActive:isActive
  214. withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
  215. error:&error];
  216. if(!success || error){
  217. error = [NSError errorWithDomain:NSEaseLocalizedString(@"error.initPlayerFail", @"Failed to initialize AVAudioPlayer")
  218. code:-1
  219. userInfo:nil];
  220. return error;
  221. }
  222. }
  223. _currCategory = audioSessionCategory;
  224. return error;
  225. }
  226. #pragma mark - Convert
  227. - (BOOL)convertAMR:(NSString *)amrFilePath
  228. toWAV:(NSString *)wavFilePath
  229. {
  230. BOOL ret = NO;
  231. BOOL isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:amrFilePath];
  232. if (isFileExists) {
  233. [EMVoiceConverter amrToWav:amrFilePath wavSavePath:wavFilePath];
  234. isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:wavFilePath];
  235. if (isFileExists) {
  236. ret = YES;
  237. }
  238. }
  239. return ret;
  240. }
  241. - (BOOL)convertWAV:(NSString *)wavFilePath
  242. toAMR:(NSString *)amrFilePath {
  243. BOOL ret = NO;
  244. BOOL isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:wavFilePath];
  245. if (isFileExists) {
  246. [EMVoiceConverter wavToAmr:wavFilePath amrSavePath:amrFilePath];
  247. isFileExists = [[NSFileManager defaultManager] fileExistsAtPath:amrFilePath];
  248. if (!isFileExists) {
  249. } else {
  250. ret = YES;
  251. }
  252. }
  253. return ret;
  254. }
  255. @end