iOS 錄音,播放并上傳

1.界面布局,以及相關功能


點擊中間開始錄音,點擊左上角播放或暫停播放,點擊右上角移除文件


2.定義相關屬性


#import "SendVoiceController.h"

#import#import "RecordVoiceView.h"

#import "lame.h"

#import "PlayVoiceView.h"

#define cafFilePathName @"myRecordForCaf.caf"

#define mp3FilePathName @"myRecordForMp3.mp3"

@interface SendVoiceController ()//錄音存儲路徑

@property (nonatomic, strong)NSURL *tmpFile;

//錄音

@property (nonatomic, strong)AVAudioRecorder *recorder;

//播放

@property (nonatomic, strong)AVAudioPlayer *player;

//錄音動畫

@property(nonatomic,strong)RecordVoiceView *recordview;

@property(strong,nonatomic)PlayVoiceView *playview;

//錄音計時器

@property(nonatomic,strong)NSTimer *recordTimer;

//錄音秒數(shù)

@property(strong,nonatomic)NSTimer *timer;

@property(nonatomic,assign)int index;

@property(nonatomic,assign)int duration;

@end

@implementation SendVoiceController

3.創(chuàng)建audiosession

-(void)setAVRecorder{

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

urlStr=[urlStr stringByAppendingPathComponent:cafFilePathName];

self.tmpFile = [NSURL fileURLWithPath:urlStr];

//設置后臺播放

AVAudioSession *session = [AVAudioSession sharedInstance];

NSError *sessionError;

//AVAudioSessionCategoryPlayAndRecord,這個確保了既能錄音也能播放

[session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];

//判斷后臺有沒有播放

if (session == nil) {

NSLog(@"Error creating sessing:%@", [sessionError description]);

} else {

[session setActive:YES error:nil];

}

}

4.中間圖片手勢代理方法

//長按事件的實現(xiàn)方法

- (void) handleTableviewCellLongPressed:(UILongPressGestureRecognizer *)gestureRecognizer {

if (gestureRecognizer.state ==

UIGestureRecognizerStateBegan) {

NSLog(@"UIGestureRecognizerStateBegan");

[self recorderPressing];

}

if (gestureRecognizer.state ==

UIGestureRecognizerStateChanged) {

NSLog(@"UIGestureRecognizerStateChanged");

}

if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {

NSLog(@"UIGestureRecognizerStateEnded");

[self recorderEnd];

}

}


5.創(chuàng)建AVAudioRecorder并開始錄音

-(void)recorderPressing{

NSDictionary *settingdic = [NSDictionary dictionaryWithObjectsAndKeys:

[NSNumber numberWithFloat:8000],? ? ? ? ? ? ? ? ? AVSampleRateKey, // 電話所用采樣率

[NSNumber numberWithInt:kAudioFormatLinearPCM],? ? AVFormatIDKey,

[NSNumber numberWithInt:2],? ? ? ? ? ? ? ? ? ? ? AVNumberOfChannelsKey,

[NSNumber numberWithInt:16],? ? ? ? ? ? ? ? ? ? ? AVLinearPCMBitDepthKey,

[NSNumber numberWithInt:AVAudioQualityMin],? ? ? AVEncoderAudioQualityKey,

nil];

//開始錄音,將所獲取到得錄音存到文件里

//開始錄音,將所獲取到得錄音存到文件里

self.recorder = [[AVAudioRecorder alloc] initWithURL:_tmpFile settings:settingdic error:nil];

self.recorder.meteringEnabled = YES;

self.recorder.delegate=self;

//準備記錄錄音

[self.recorder prepareToRecord];

//啟動或者恢復記錄的錄音文件

[self.recorder record];

self.player = nil;

//以下是我仿照衛(wèi)星發(fā)語音的動畫效果

self.recordview.hidden=NO;

self.recordTimer=[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(recordTimerChange) userInfo:nil repeats:YES];

}

6.中間圖片長按取消的同時關閉錄音

-(void)recorderEnd{

self.duration=self.index;

self.index=0;

//停止錄音

if (self.recordTimer) {

[self.recordTimer invalidate];

self.recordTimer=nil;

}

[self.recorder stop];

self.recordview.hidden=YES;

self.recorder = nil;

self.playBtn.hidden=NO;

self.removeBtn.hidden=NO;

self.curveLineImage.hidden=NO;

NSError *playError;

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

urlStr=[urlStr stringByAppendingPathComponent:mp3FilePathName];

NSURL *url = [NSURL fileURLWithPath:urlStr];

self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&playError];

//當播放錄音為空, 打印錯誤信息

if (self.player == nil) {

NSLog(@"Error crenting player: %@", [playError description]);

}

self.player.delegate = self;

//? ? [self.player play];

}


7.與其同時觸發(fā)avaudiorecord代理方法

這里面的主要作用是把錄音的caf文件轉(zhuǎn)成mp3文件,用于上傳到服務器,其中的方法是來自于Libmo3lame庫

#pragma mark---錄音代理

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *cafFilePath =[urlStr stringByAppendingPathComponent:cafFilePathName];? ? //caf文件路徑

NSString *mp3FilePath = [urlStr stringByAppendingPathComponent:mp3FilePathName]; ;//存儲mp3文件的路徑

NSFileManager * fileManager=[ NSFileManager defaultManager ];

if ([fileManager fileExistsAtPath:cafFilePath]){

float msize= [[fileManager attributesOfItemAtPath:cafFilePath error:nil] fileSize]/(1024.0*1024.0);

NSLog(@"caf文件大小%lf",msize);

}else{

float msize= [[fileManager attributesOfItemAtPath:mp3FilePathName error:nil] fileSize]/(1024.0*1024.0);

NSLog(@"mp3文件大小%lf",msize);

}

if ([fileManager removeItemAtPath :mp3FilePath error : nil ])

{

NSLog ( @" 刪除 " );

}

@try {

int read, write;

FILE *pcm = fopen ([cafFilePath cStringUsingEncoding : 1 ], "rb" );? //source 被 轉(zhuǎn)換的音頻文件位置

if (pcm == NULL )

{

NSLog ( @"file not found" );

}

else

{

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_num_channels (lame, 2 ); // 設置 1 為單通道,默認為 2 雙通道

lame_set_in_samplerate (lame,? 8000.0 ); //11025.0

//lame_set_VBR(lame, vbr_default);

lame_set_brate (lame, 16);

lame_set_mode (lame, 3 );

lame_set_quality (lame, 2 ); /* 2=high 5 = medium 7=low 音 質(zhì) */

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);

return? ;

}

return? ;

}

@catch (NSException *exception) {

NSLog ( @"%@" ,[exception description ]);

return? ;

}

@finally {

NSLog ( @" 執(zhí)行完成 " );

}

}

8.左上角按鈕事件

-(IBAction)playerClick:(id)sender{

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString * path=[urlStr stringByAppendingPathComponent:cafFilePathName];

NSFileManager* manager = [NSFileManager defaultManager];

if ([manager fileExistsAtPath:path]){

float msize= [[manager attributesOfItemAtPath:path error:nil] fileSize]/(1024.0*1024.0);

NSLog(@"文件大小%lf",msize);

}

else{

_player=nil;

}

//判斷是否正在播放,如果正在播放

if ([self.player isPlaying]) {

//暫停播放

[_player pause];

[self.playBtn? setImage:[UIImage imageNamed:@"voice_play"] forState:UIControlStateNormal];

//按鈕顯示為播放

} else {

//開始播放

[_player play];

[self.playBtn? setImage:[UIImage imageNamed:@"voice_pause"] forState:UIControlStateNormal];

}

}


9.右上角按鈕事件

-(IBAction)removeClick:(id)sender{

NSIndexPath *indexpath=[NSIndexPath indexPathForItem:6 inSection:0];

self.playBtn.hidden=YES;

self.removeBtn.hidden=YES;

self.curveLineImage.hidden=YES;

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *cafFilePath =[urlStr stringByAppendingPathComponent:cafFilePathName];? ? //caf文件路徑

NSString *mp3FilePath = [urlStr stringByAppendingPathComponent:mp3FilePathName]; ;//存儲mp3文件的路徑

NSFileManager * fileManager=[ NSFileManager defaultManager ];

if ([fileManager removeItemAtPath :mp3FilePath error : nil ])

{

NSLog ( @" 刪除 mp3" );

}

if ([fileManager removeItemAtPath :cafFilePath error : nil ])

{

NSLog ( @" 刪除 caf" );

}

}

10.上傳到服務器

主要的邏輯是把MP3文件轉(zhuǎn)成nsdata,然后通過base64string 轉(zhuǎn)成字符串傳給后臺

NSString *url=[[HJInterfaceManager sharedInstance]addRichInfo];

NSMutableDictionary *mdic=[[NSMutableDictionary alloc]initWithCapacity:0];

NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *mp3FilePath = [urlStr stringByAppendingPathComponent:mp3FilePathName]; ;//

//? ? NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"];

NSData *filedata=[NSData dataWithContentsOfFile:mp3FilePath];

SelfDataModel *selfModel=[SelfDataModel returnModelBySelectFMDB];

//? ? ? ? * @param familyId

//? ? ? ? * @param userId

//? ? ? ? * @param content

//? ? ? ? * @param richContent

//? ? ? ? * @param type

//? ? ? ? * @param sendTime

NSDate *date=[NSDate date];

NSDateFormatter *form=[[NSDateFormatter alloc]init];

[form setDateFormat:@"YYYY-MM-dd HH:mm:ss"];

NSString *datestr=[form stringFromDate:date ];

if (!self.isMyself) {

[mdic setObject:self.family.idNum forKey:@"familyId"];

}

[mdic setObject:selfModel.idNum forKey:@"userId"];

[mdic setObject:@"" forKey:@"content"];

[mdic setObject:[NSString stringWithFormat:@"%d",self.duration] forKey:@"duration"];

[mdic setObject:[filedata base64Encoding] forKey:@"richContents"];

[mdic setObject:@"SOUND" forKey:@"type"];

[mdic setObject:datestr forKey:@"sendTime"];

[HJHttpManager PostRequestWithUrl:url param:mdic finish:^(NSData *data) {

NSDictionary *dic=(NSDictionary *)data;

if ([dic[@"status"] isEqualToString:@"S"]) {

[MBProgressHUD showSuccess:@"發(fā)送成功"];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[self.navigationController popViewControllerAnimated:YES];

});

}else{

[MBProgressHUD showError:dic[@"message"]];

}

} failed:^(NSError *error) {

NSLog(@"請求失敗");

}];


小帖士:

以下是監(jiān)聽聲音分貝的方法

#pragma mark----監(jiān)聽聲音分貝

/* 該方法確實會隨環(huán)境音量變化而變化,但具體分貝值是否準確暫時沒有研究 */

- (void)recordTimerChange {

//? ? [self.recorder updateMeters];//刷新音量數(shù)據(jù)

//? ? double lowPassResults = pow(10, (0.05 * [self.recorder peakPowerForChannel:0]));

//? ? NSLog(@"大小:%lf",lowPassResults);

self.recordview.timeLab.text=[NSString stringWithFormat:@"%d s",self.index];

self.index++;

[self.recorder updateMeters];

float? level;? ? ? ? ? ? ? ? // The linear 0.0 .. 1.0 value we need.

float? minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room.

float? decibels? ? = [self.recorder averagePowerForChannel:0];

if (decibels < minDecibels)

{

level = 0.0f;

}

else if (decibels >= 0.0f)

{

level = 1.0f;

}

else

{

float? root? ? ? ? ? ? = 2.0f;

float? minAmp? ? ? ? ? = powf(10.0f, 0.05f * minDecibels);

float? inverseAmpRange = 1.0f / (1.0f - minAmp);

float? amp? ? ? ? ? ? = powf(10.0f, 0.05f * decibels);

float? adjAmp? ? ? ? ? = (amp - minAmp) * inverseAmpRange;

level = powf(adjAmp, 1.0f / root);

}

float ben=level*120;

if (ben<20) {

[self.recordview.imageview setImage:[UIImage imageNamed:@"order_voice_1"]];

}

else if (ben<35) {

[self.recordview.imageview setImage:[UIImage imageNamed:@"order_voice_2"]];

}

else{

[self.recordview.imageview setImage:[UIImage imageNamed:@"order_voice_3"]];

}

NSLog(@"大小%lf",ben);

}

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容