第一類:有的軟件會播放背景音樂和音效,並且可以控制音樂和音效的開關控制,下面進行這種效果的分析
工具類創建需求
1.創建一個專門管理音樂和音效的單例類
2.設置播放音樂、音效的兩個屬性方法,和兩個是否播放音樂、音效的BOOL屬性。(音樂播放方法是爲了在APPDelegate中調用)
3.設置兩個BOOL屬性的get方法,這樣當外界更改這兩個方法的時候,可以立即做出對應的改變,在get方法中最對應的相關操作 e.g.使用對象序列化來保存狀態,音樂的開關可以在這裏進行開關操作。
當需要播放音樂的時候調用屬性方法,在屬性方法裏面進行音樂BOOL屬性的判斷,如果不播放直接return,如果播放則調用播放方法。(音樂播放不需要傳入音樂名稱,因爲一般爲固定的一個)
當需要播放音效的時候調用屬性方法,在屬性方法裏面進行音效BOOL屬性的判斷,如果不播放直接return,如果播放則調用音效方法。(音效播放需要傳入音效名稱)
工具類初始化
在這個工具類裏面初始化時需要需要調用直接調用加載音樂和加載音效的方法,兩個BOOL屬性值需要從對象序列化中取出,取出時就會調用get方法。e.g.音樂是否播放在這裏設置
音效的播放每次都要傳入要播放的文件名
專門寫一個.h文件把音效,音樂等文件名寫成一個#define來防止寫錯。
加載資源的時候一次全部加載完成
#pragma mark 加載背景音樂
- (void)loadBgMusic
{
// 1.獲得背景音樂的URL
NSURL *url = [[NSBundle mainBundle] URLForResource:@"bg_music.mp3" withExtension:nil];
// 2.加載播放器
_bgMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
// 緩衝
[_bgMusicPlayer prepareToPlay];
// 無限循環
_bgMusicPlayer.numberOfLoops = -1;
}
#pragma mark 加載音效 一次全部加載完
- (void)loadSounds
{
// 1.初始化字典
_soundIDs = [NSMutableDictionary dictionary];
// 2.加載sound.bundle
NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"sound" withExtension:@"bundle"];
NSBundle *soundBundle = [NSBundle bundleWithURL:bundleURL];
// 3.獲得sound.bundle中所有mp3文件的URL
NSArray *mp3URLs = [soundBundle URLsForResourcesWithExtension:@"mp3" subdirectory:nil];
// 4.根據每個mp3的URL加載soundID
for (NSURL *url in mp3URLs)
{
SystemSoundID soundID;
// 5.加載音頻ID
AudioServicesCreateSystemSoundID((__bridge CFURLRef )url, &soundID);
// 6.獲得文件名
NSString *filename = [url.path lastPathComponent];
// 7.將音頻ID和文件名放入字典
[_soundIDs setObject:@(soundID) forKey:filename];
}
}
第二類:音樂播放APP
寫一個工具類專門進行播放、暫停、停止等操作
再專門寫一個工具類進行上一首、下一首操作
#pragma mark - 傳入需要播放的音效文件
+(void)playAudioWithFileName:(NSString *)fileName
{
//容錯判斷
if (fileName == nil) return;
//取出音效ID
SystemSoundID soundID = [[self soundIDs][fileName] unsignedIntValue];
//容錯判斷
if (!soundID)
{
//創建URL
NSURL *url = [[NSBundle mainBundle] URLForResource:fileName withExtension:nil];
//容錯判斷
if (!url) return;
//創建音效ID 傳一個URL 一個SystemSoundID 報錯fix就好
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
//添加到字典中
[self soundIDs][fileName] = @(soundID);
}
// 播放音效
// AudioServicesPlaySystemSound(soundID);
// 播放音效帶點震動
AudioServicesPlayAlertSound(soundID);
}
#pragma mark - 銷燬音效文件
+(void)disposeAudioWithFileName:(NSString *)fileName
{
//容錯判斷
if (!fileName) return;
//從字典中取出音效ID
SystemSoundID soundID = [[self soundIDs][fileName] unsignedIntValue];
//容錯判斷
if (soundID)
{
//銷燬音效ID
AudioServicesDisposeSystemSoundID(soundID);
//從字典中移除
[[self soundIDs] removeObjectForKey:fileName];
}
}
#pragma mark - 根據音樂文件播放音樂文件
+(AVAudioPlayer *)playMusicWithFileName:(NSString *)fileName
{
if (!fileName) return nil;
//從字典中取出播放器
AVAudioPlayer *player = [self players][fileName];
//判斷播放器是否爲nil
if (!player) {
//根據文件名加載音效URL
NSURL *url = [[NSBundle mainBundle] URLForResource:fileName withExtension:nil];
if (!url) return nil;
//創建播放器
player = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
// NSLog(@"playMusicWithFileName");
//準備播放(緩衝提高播放的流暢性)
if (![player prepareToPlay]) return nil;
// 允許快進
// player.enableRate = YES;
// player.rate = 3;
//添加到字典中
[self players][fileName] = player;
}
//判斷是否在播放
if (!player.isPlaying) {
//播放(異步播放)
[player play];
}
return player;
/*
時長
@property(readonly) NSTimeInterval duration;
當前的播放位置
@property NSTimeInterval currentTime;
播放次數(-1代表無限循環播放,其他代表播放numberOfLoops+1次)
@property NSInteger numberOfLoops;
音量
@property float volume;
是否允許更改速率
@property BOOL enableRate;
播放速率(1是正常速率,0.5是一般速率,2是雙倍速率)
@property float rate;
有多少個聲道
@property(readonly) NSUInteger numberOfChannels;
聲道(-1是左聲道,1是右聲道,0是中間)
@property float pan;
是否允許測量音量
@property(getter=isMeteringEnabled) BOOL meteringEnabled;
更新測量值
- (void)updateMeters;
獲得當前的平均音量
- (float)averagePowerForChannel:(NSUInteger)channelNumber;
*/
}
#pragma mark - 根據音樂名暫停播放音樂文件
+(void)pauseMusicWithFileName:(NSString *)fileName
{
if (fileName == nil) return;
//從字典中取出播放器
AVAudioPlayer *player = [self players][fileName];
if (player)
{
//如果正在播放就停止播放
if (player.playing)
{
[player pause];
}
}
}
#pragma mark - 根據音樂名停止播放音樂文件
+(void)stopMusicWithFileName:(NSString *)fileName
{
if (!fileName) return;
//從字典中取出播放器
AVAudioPlayer *player = [self players][fileName];
if (player)
{
[player stop];
player = nil;
//從字典中移除播放器
[[self players] removeObjectForKey:fileName];
}
}
後臺播放
-(void)applicationDidEnterBackground:(UIApplication *)application
{
//開啓後臺任務
UIBackgroundTaskIdentifier identifier = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:identifier];
}];
//在plist文件中添加 Required background modes 設爲音樂
//設置會話類型讓我放的時候把其他停掉
}
在工具類中設置
//設置會話類型 //這個方法保證只在第一次創建時調用
+(void)initialize
{
NSLog(@"initialize");
//創建音頻會話
AVAudioSession *session = [[AVAudioSession alloc]init];
//設置會話類型
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
//激活會話
[session setActive:YES error:nil];
}
打斷操作
#pragma mark - AVAudioPlayerDelegate
// 播放結束時調用
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// 下一曲
[self downMusic:nil];
}
// 播放器被打斷時調用(例如電話)
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
// 暫停
if (self.audioPlayer.playing) {
[QCFAudioTool pauseMusicWithFileName:[_array objectAtIndex:_index]];
}
}
// 播放器打斷結束
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
// 繼續播放
if (!self.audioPlayer.playing) {
[self play:nil];
}
}
鎖屏界面顯示內容
#pragma mark - 在鎖屏中添加內容 更新也會調用
-(void)lockContext
{
// 1.播放信息中心
MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
// 2.初始化播放信息
NSMutableDictionary *info = [NSMutableDictionary dictionary];
//鎖屏上面顯示的信息
info[MPMediaItemPropertyAlbumTitle] = @"專輯名稱";
info[MPMediaItemPropertyArtist] = @"歌手";
info[MPMediaItemPropertyTitle] = @"歌曲名稱";
info[MPMediaItemPropertyArtwork] = [[MPMediaItemArtwork alloc]initWithImage:[UIImage imageNamed:@"123"]];
//顯示文字就是把文字畫在圖片上
//設置持續時間(歌曲總時間)
info[MPMediaItemPropertyPlaybackDuration] = @"12.12";
//設置當前播放的進度
info[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @"22";
// 3.切換播放信息
center.nowPlayingInfo = info;
// 4.開始監聽遠程事件
// 4.1 成爲第一相應者 (必須設置)
[self becomeFirstResponder];
// 4.2 開始監控
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
#pragma mark - 遠程控制事件監聽
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)remoteControlReceivedWithEvent:(UIEvent *)event
{
// event.subtype;//事件的子類型
/*
UIEventSubtypeNone
UIEventSubtypeMotionShake
UIEventSubtypeRemoteControlPlay
UIEventSubtypeRemoteControlPause
UIEventSubtypeRemoteControlStop
UIEventSubtypeRemoteControlTogglePlayPause
UIEventSubtypeRemoteControlNextTrack
UIEventSubtypeRemoteControlPreviousTrack
UIEventSubtypeRemoteControlBeginSeekingBackward
UIEventSubtypeRemoteControlEndSeekingBackward
UIEventSubtypeRemoteControlBeginSeekingForward
UIEventSubtypeRemoteControlEndSeekingForward
*/
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
{
NSLog(@"播放點擊");
}break;
case UIEventSubtypeRemoteControlPause:
{
NSLog(@"暫停點擊");
}break;
case UIEventSubtypeRemoteControlNextTrack:
{
NSLog(@">>>下一首點擊");
}break;
case UIEventSubtypeRemoteControlPreviousTrack:
{
NSLog(@"<<<上一首點擊");
}break;
default: break;
}
}