學(xué)習(xí)筆記----音頻開發(fā)基礎(chǔ)

參考鏈接

一、AVAudioPlayer播放本地音樂

簡介:AVAudioPlayer是AVFoundation.framework中的一個類胆描,它支持多種音頻格式,而且能夠進(jìn)行進(jìn)度国夜、音量短绸、播放速度等控制醋闭,詳細(xì)屬性可以看
原文 介紹非常詳細(xì)。

注意點:AVAudioPlayer只能播放本地文件乐埠,并且是一次性加載所有音頻數(shù)據(jù)瑟曲,初始化AVAudioPlayer時指定的URL也只能是File URL而不能是HTTP URL,AVAudioPlayer一次只能播放一個音頻文件,如果想實現(xiàn)上一曲烦衣、下一曲的功能可以通過創(chuàng)建多個播放器對象來完成.

AVAudioPlayer簡單使用

1.初始化一個AVAudioPlayer

  //獲取本地音樂的路徑
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"235319" ofType:@"mp3"];    

    NSURL *url2 = [NSURL URLWithString:filePath];

    NSError *error = nil;
      //創(chuàng)建音樂播放器掩浙,此URL不能是HTTP URL
    _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url2 error:&error];

    //設(shè)置為0不循環(huán)厨姚,可以設(shè)置循環(huán)次數(shù)
    _audioPlayer.numberOfLoops=3;

    //根據(jù)URL地址來讀取音樂文件(寫在ViewDidLoad中會自動播放)
    [_audioPlayer prepareToPlay];
    
    //設(shè)置代理,監(jiān)聽音樂是否播放完成
    _audioPlayer.delegate = self;

    //error存在今布,初始化失敗
    if (error) {
        NSLog(@"初始化錯誤");
    }

2.在需要播放的地方開始播放就行了

-(void)playClick:(UIButton*)button{
    
    if (!button.selected) {
        [self play];
    }else{
        [self pause];
    }
    button.selected = !button.selected;
}

-(void)play{
    if (![_audioPlayer isPlaying]) {
        [_audioPlayer play];
        self.timer.fireDate=[NSDate distantPast];//恢復(fù)定時器
    }
}

/**
 *  暫停播放
 */
-(void)pause{
    if ([_audioPlayer isPlaying]) {
        [_audioPlayer pause];
        self.timer.fireDate=[NSDate distantFuture];//暫停定時器拭抬,注意不能調(diào)用invalidate方法造虎,此方法會取消,之后無法恢復(fù)
    }
}

3.滑動UISlider設(shè)置當(dāng)前播放
音樂播放進(jìn)度的實現(xiàn)主要依靠一個定時器實時計算當(dāng)前播放時長和音頻總時長的比例

//開始播放時份蝴,開始定時器,暫停播放時暫停定時器
-(NSTimer *)timer{
    if (!_timer) {
        _timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:true];
    }
    return _timer;
}

-(void)updateProgress{
 //當(dāng)前播放音樂的進(jìn)度時間/音樂總時間波桩,算出值賦值給Slider
    float progress= _audioPlayer.currentTime /_audioPlayer.duration;
    if (!self.isTap) {
        [self.slider setValue:progress animated:true];
    }
}
-(void)playToTime:(float)value{
    if (![_audioPlayer isPlaying]) {
        _audioPlayer.currentTime = value*_audioPlayer.duration;

        [_audioPlayer play];
        self.timer.fireDate=[NSDate distantPast];//恢復(fù)定時器
    }else{
        _audioPlayer.currentTime = value*_audioPlayer.duration;
    }
}

4.完成播放回掉代理

#pragma mark - 播放器代理方法
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    NSLog(@"音樂播放完成...");
//切換歌曲可以在此操作
    self.timer.fireDate=[NSDate distantFuture];//恢復(fù)定時器
}

二镐躲、AVPlayer播放網(wǎng)絡(luò)侍筛,本地音樂

參考文章鏈接:http://www.cnblogs.com/kenshincui/p/4186022.html

簡介:上面說了AVAudioPlayer 不支持網(wǎng)絡(luò)音頻在線播放匣椰,你可以使用一些第三方框架來實現(xiàn)在線播放例如AudioStreamerFreeStreamer入录。如果不想使用第三方的框架佳镜,AVPlayer可以幫你實現(xiàn)在線播放的功能,AVPlayer也是存在于AVFoundation中蚀同,但是它功能要更加強(qiáng)大蠢络,可以播放本地音視頻和網(wǎng)絡(luò)音視頻迟蜜,有時當(dāng)你需要自定義一些播放樣式時,蘋果提供的一些封裝好的API例如MPMoviePlayerController可能并不能滿足你的需求髓霞,這時使用AVPlayer可以幫你完成這些需求微姊。

常用的類:

  • AVAsset:主要用于獲取多媒體信息兢交,是一個抽象類,不能直接使用酪穿。
  • AVURLAsset:AVAsset的子類,可以根據(jù)一個URL路徑創(chuàng)建一個包含媒體信息的AVURLAsset對象救赐。
  • AVPlayerItem:一個媒體資源管理對象只磷,管理者視頻的一些基本信息和狀態(tài),一個AVPlayerItem對應(yīng)著一個視頻資源预厌。

暫停轧叽、播放功能

首先說一下音頻的播放刊棕、暫停功能,這也是最基本的功能网严,AVPlayer對應(yīng)著兩個方法play蜈膨、pause來實現(xiàn)翁巍。但是關(guān)鍵問題是如何判斷當(dāng)前音頻是否在播放休雌,在前面的內(nèi)容中無論是音頻播放器還是音頻播放器都有對應(yīng)的狀態(tài)來判斷,但是AVPlayer卻沒有這樣的狀態(tài)屬性驰凛,通常情況下可以通過判斷播放器的播放速度來獲得播放狀態(tài)恰响。如果rate為0說明是停止?fàn)顟B(tài)涌献,1是則是正常播放狀態(tài)。

///判斷是否正在播放
-(BOOL)isPlaying{
    if (self.rate==1) {
        return YES;
    }else{
        return NO;
    }
}

播放進(jìn)度的控制和音頻的切換

其次要展示播放進(jìn)度就沒有其他播放器那么簡單了枢劝。在前面的播放器中通常是使用通知來獲得播放器的狀態(tài),媒體加載狀態(tài)等烙常,但是無論是AVPlayer還是AVPlayerItem(AVPlayer有一個屬性currentItem是AVPlayerItem類型蚕脏,表示當(dāng)前播放的音頻對象)都無法獲得這些信息侦锯。當(dāng)然AVPlayerItem是有通知的率触,但是對于獲得播放狀態(tài)和加載狀態(tài)有用的通知只有一個(其他真的沒啥用):播放完成通知AVPlayerItemDidPlayToEndTimeNotification。在播放音頻時穴张,特別是播放網(wǎng)絡(luò)音頻往往需要知道音頻加載情況两曼、緩沖情況、播放情況偿枕,這些信息可以通過KVO監(jiān)控AVPlayerItem的status渐夸、loadedTimeRanges屬性來獲得渔欢。當(dāng)AVPlayerItem的status屬性為AVPlayerStatusReadyToPlay是說明正在播放,只有處于這個狀態(tài)時才能獲得音頻時長等信息苫幢;當(dāng)loadedTimeRanges的改變時(每緩沖一部分?jǐn)?shù)據(jù)就會更新此屬性)可以獲得本次緩沖加載的音頻范圍(包含起始時間垫挨、本次加載時長)九榔,這樣一來就可以實時獲得緩沖情況涡相。然后就是依靠AVPlayer的方法獲得播放進(jìn)度漾峡,這個方法會在設(shè)定的時間間隔內(nèi)定時更新播放進(jìn)度喻旷,通過time參數(shù)通知客戶端。相信有了這些視頻信息播放進(jìn)度就不成問題了槽袄,事實上通過這些信息就算是平時看到的其他播放器的緩沖進(jìn)度顯示以及拖動播放的功能也可以順利的實現(xiàn)遍尺。

最后就是音頻切換的功能涮拗,在前面介紹的所有播放器中每個播放器對象一次只能播放一個音頻,如果要切換音頻只能重新創(chuàng)建一個對象鼓择,但是AVPlayer卻提供了- (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item方法用于在不同的音頻之間切換(事實上在AVFoundation內(nèi)部還有一個AVQueuePlayer專門處理播放列表切換呐能,有興趣的朋友可以自行研究抑堡,這里不再贅述)首妖。

添加通知和觀察者

///添加通知和觀察者
-(void)addNotificationAndObserver{
    [self addNotification];
    [self addProgressObserver];
    [self addObserverToPlayerItem:self.currentItem];
}

///移除所有的通知和觀察者
-(void)removeNotificationAndObserver{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem];
    [self removeObserverFromPlayerItem:self.currentItem];
    [self removeTimeObserver:self.obeserObject];
}


-(void)addProgressObserver{
    __weak typeof(self) weakSelf = self;
    //這里設(shè)置每秒執(zhí)行一次,這個方法會在設(shè)定的時間間隔內(nèi)定時更新播放進(jìn)度有缆,通過time參數(shù)通知客戶端。
    self.obeserObject = [self addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        if ([weakSelf.delegate respondsToSelector:@selector(msPlyaer:cmTime:)]) {
            [weakSelf.delegate msPlyaer:weakSelf cmTime:time];
        }
    }];
}

-(void)addNotification{
    //給AVPlayerItem添加播放完成通知(音樂播放完成后會回調(diào)通知方法)
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem];
}


-(void)playbackFinished:(NSNotification *)notification{
    self.isPlayComplete = YES;
    if ([self.delegate respondsToSelector:@selector(msPlyaerPlayEnd:)]) {
        [self.delegate msPlyaerPlayEnd:self];
    }
}

-(void)addObserverToPlayerItem:(AVPlayerItem *)playerItem{
    //監(jiān)控狀態(tài)屬性,注意AVPlayer也有一個status屬性灌曙,通過監(jiān)控它的status也可以獲得播放狀態(tài)
    
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //監(jiān)控網(wǎng)絡(luò)加載情況屬性
    [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    
}
-(void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{
    [playerItem removeObserver:self forKeyPath:@"status"];
    [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}


-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    AVPlayerItem *playerItem=object;
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerStatus status= [[change objectForKey:@"new"] intValue];

        if(status==AVPlayerStatusReadyToPlay){
            ///獲取音頻長度
            NSLog(@"正在播放...在刺,視頻總長度:%.2f",CMTimeGetSeconds(playerItem.duration));
            if ([self.delegate respondsToSelector:@selector(msPlyaer:currentPlayerItem:)]) {
                [self.delegate msPlyaer:self currentPlayerItem:playerItem];
            }
        }else{
            ///更新音頻播放時間
            if ([self.delegate respondsToSelector:@selector(msPlyaerPlayError:currentPlayerItem:)]) {
                [self.delegate msPlyaerPlayError:self currentPlayerItem:playerItem];
            }
        }
    }else if([keyPath isEqualToString:@"loadedTimeRanges"]){
        NSArray *array=playerItem.loadedTimeRanges;
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度
        NSLog(@"共緩沖:%.2f",totalBuffer);
        //
    }
}

音量控制

使用player.volume屬性可以控制播放的音量蚣驼,但是此屬性僅改變app的音量颖杏,不要用它來制作slider的聲音控制開關(guān),可以使用MPVolumeView來制作翼抠,這里 就不介紹啦获讳,有興趣的朋友可以自己去看看丐膝。

注意點

1.切換音樂時可以手動判斷當(dāng)前是否在播放,以防止上一曲暫唾怂啵或者播放完成后處于暫停狀態(tài)下直接調(diào)用下面方法時不播放损晤。

///調(diào)用此方法切換播放源
    [self replaceCurrentItemWithPlayerItem:item];

2.切換音頻時要清除上一個item的通知和觀察者

///移除所有的通知和觀察者
-(void)removeNotificationAndObserver{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem];
    [self removeObserverFromPlayerItem:self.currentItem];
    [self removeTimeObserver:self.obeserObject];
}

3.使用全局變量標(biāo)記進(jìn)度觀察者返回的對象,切換音頻時移除觀察


@interface MSPlayer ()

@property(nonatomic,strong) id  obeserObject;

@end
-(void)addProgressObserver{
    __weak typeof(self) weakSelf = self;
    //這里設(shè)置每秒執(zhí)行一次,這個方法會在設(shè)定的時間間隔內(nèi)定時更新播放進(jìn)度尤勋,通過time參數(shù)通知客戶端最冰。
    self.obeserObject = [self addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        if ([weakSelf.delegate respondsToSelector:@selector(msPlyaer:cmTime:)]) {
            [weakSelf.delegate msPlyaer:weakSelf cmTime:time];
        }
    }];
}
   [self removeTimeObserver:self.obeserObject];

4.音頻播放失敗后一定要重新處理player和item,具體的好辦法我也不清楚赌朋,只是通過下面的代碼實現(xiàn)了重新播放沛慢。

-(void)msPlyaerPlayError:(MSPlayer*)player currentPlayerItem:(AVPlayerItem*)playerItem{
    self.player = nil;
    self.playerItme = nil;
}

還有一些注意項我也忘記了达布,demo里面都寫了很 詳細(xì)的注釋,這只是我們項目中用到音頻播放這一塊時寫的一個小demo躺苦,有興趣的朋友可以看一下,寫的不好不喜勿噴匹厘。

參考文章鏈接:http://www.cnblogs.com/kenshincui/p/4186022.html

demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末愈诚,一起剝皮案震驚了整個濱河市扰路,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宫莱,老刑警劉巖哩罪,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件际插,死亡現(xiàn)場離奇詭異,居然都是意外死亡辛辨,警方通過查閱死者的電腦和手機(jī)斗搞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門慷妙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膝擂,“玉大人,你說我怎么就攤上這事狞山∠衬” “怎么了办绝?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長孕蝉。 經(jīng)常有香客問我屡律,道長,這世上最難降的妖魔是什么降淮? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任超埋,我火速辦了婚禮,結(jié)果婚禮上佳鳖,老公的妹妹穿的比我還像新娘霍殴。我一直安慰自己,他們只是感情好系吩,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布来庭。 她就那樣靜靜地躺著穿挨,像睡著了一般月弛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上科盛,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天帽衙,我揣著相機(jī)與錄音,去河邊找鬼贞绵。 笑死厉萝,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的榨崩。 我是一名探鬼主播谴垫,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蜡饵!你這毒婦竟也來了弹渔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤溯祸,失蹤者是張志新(化名)和其女友劉穎肢专,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焦辅,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡博杖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了筷登。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剃根。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖前方,靈堂內(nèi)的尸體忽然破棺而出狈醉,到底是詐尸還是另有隱情廉油,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布苗傅,位于F島的核電站抒线,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渣慕。R本人自食惡果不足惜嘶炭,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逊桦。 院中可真熱鬧眨猎,春花似錦、人聲如沸强经。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夕凝。三九已至宝穗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間码秉,已是汗流浹背逮矛。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留转砖,地道東北人须鼎。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像府蔗,于是被迫代替她去往敵國和親晋控。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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