音乐播放器-小案列

原创 2015年11月20日 09:28:47

1.封装音频播放工具类

//
//  AudioTool.h


#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface AudioTool : NSObject

/**
 *  播放音效
 *
 *  @param filename 音效文件名
 */
+ (void)playSound:(NSString *)filename;

/**
 *  销毁音效
 *
 *  @param filename 音效文件名
 */
+ (void)disposeSound:(NSString *)filename;

/**
 *  播放音乐
 *
 *  @param filename 音乐文件名
 */
+ (AVAudioPlayer *)playMusic:(NSString *)filename;

/**
 *  暂停音乐
 *
 *  @param filename 音乐文件名
 */
+ (void)pauseMusic:(NSString *)filename;

/**
 *  停止音乐
 *
 *  @param filename 音乐文件名
 */
+ (void)stopMusic:(NSString *)filename;

/**
 *  当前播放的音乐播放器
 */
+ (AVAudioPlayer *)currentPlayingAudioPlayer;
@end

//
//  AudioTool.m


#import "AudioTool.h"


@implementation AudioTool

/**
 * 存放所有的音效ID
 * 字典: filename作为key,soundID作为value
 */
static NSMutableDictionary *_soundIDDict;

/**
 * 存放所有的音乐播放器
 * 字典: filename作为key,audioPlayer作为value
 */
static NSMutableDictionary *_audioPlayerDict;


/**
 *  初始化
 */
+ (void)initialize
{
    _soundIDDict = [NSMutableDictionary dictionary];
    _audioPlayerDict = [NSMutableDictionary dictionary];
    
    //设置音频回话类型
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
    [session setActive:YES error:nil];
}

/**
 *  播放音效
 *
 *  @param filename 音效文件名
 */
+ (void)playSound:(NSString *)filename
{
    if (!filename) return;
    
    //1.从字典中取出soundID
    SystemSoundID soundID = [_soundIDDict[filename] unsignedIntValue];
    if (!soundID) { //创建
        
        //加载音效文件
        NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
        if(!url) return;
        
        //创建音效ID
        AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
        
        //存入字典
        _soundIDDict[filename] = @(soundID);
    }
   
    //2.播放
    AudioServicesPlaySystemSound(soundID);
    
}

/**
 *  销毁音效
 *
 *  @param filename 音效文件名
 */
+ (void)disposeSound:(NSString *)filename
{
    if (!filename) return;

    SystemSoundID soundID = [_soundIDDict[filename] unsignedIntValue];
    if (!soundID) {
        //销毁音效ID
        AudioServicesDisposeSystemSoundID(soundID);
        
        //从字典中移除
        [_soundIDDict removeObjectForKey:filename];
    }

}


/**
 *  播放音乐
 *
 *  @param filename 音乐文件名
 */
+ (AVAudioPlayer *)playMusic:(NSString *)filename
{
    if (!filename) return nil;

    //1.从字典中取出audioPlayer
    AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
    if (!audioPlayer) { //创建
        //加载音乐文件
        NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
        if(!url) return nil;
        
        //创建audioPlayer
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
        
        //缓冲
        [audioPlayer prepareToPlay];

        //存入字典
        _audioPlayerDict[filename] = audioPlayer;
    }
    
    //2.播放
    if (!audioPlayer.isPlaying) {
        [audioPlayer play];
    }
    
    return audioPlayer;
}

/**
 *  暂停音乐
 *
 *  @param filename 音乐文件名
 */
+ (void)pauseMusic:(NSString *)filename
{
    if (!filename) return;
    
    //1.从字典中取出audioPlayer
    AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
    
    //2.暂停
    if (audioPlayer.isPlaying) {
        [audioPlayer pause];
    }
}

/**
 *  停止音乐
 *
 *  @param filename 音乐文件名
 */
+ (void)stopMusic:(NSString *)filename
{
    if (!filename) return;
    
    //1.从字典中取出audioPlayer
    AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
    
    //2.暂停
    if (audioPlayer.isPlaying) {
        [audioPlayer stop];
        
        //从字典中移除
        [_audioPlayerDict removeObjectForKey:filename];
    }
}

/**
 *  当前播放的音乐播放器
 */
+ (AVAudioPlayer *)currentPlayingAudioPlayer
{
    for (NSString *filename in _audioPlayerDict) {
        AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
        if (audioPlayer.isPlaying) {
            return audioPlayer;
        }
    }
    
    return nil;
}

@end

2.要播放的音乐模型

//
//  MusicModel.h


#import <Foundation/Foundation.h>

@interface MusicModel : NSObject
/**
 *  歌曲名称
 */
@property(nonatomic,copy)NSString *name;
/**
 *  歌曲文件名称
 */
@property(nonatomic,copy)NSString *filename;
/**
 *  歌手
 */
@property(nonatomic,copy)NSString *singer;
/**
 *  歌手图片
 */
@property(nonatomic,copy)NSString *singerIcon;
/**
 *  大图
 */
@property(nonatomic,copy)NSString *icon;

@property(nonatomic,assign,getter=isPlaying)BOOL playing;


- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)musicWithDict:(NSDictionary *)dict;
@end
//
//  MusicModel.m

#import "MusicModel.h"

@implementation MusicModel
- (instancetype)initWithDict:(NSDictionary *)dict
{
    self = [super init];
    if (self) {
        self.name = dict[@"name"];
        self.filename = dict[@"filename"];
        self.singer = dict[@"singer"];
        self.singerIcon = dict[@"singerIcon"];
        self.icon = dict[@"icon"];
    }
    return self;
}

+ (instancetype)musicWithDict:(NSDictionary *)dict
{
    return [[self alloc] initWithDict:dict];
}
@end

3.控制器里(这里我们用的UITableView)

//
//  MusicViewController.m


#import "MusicViewController.h"
#import "MusicModel.h"
#import "AudioTool.h"
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "MusicCell.h"

@interface MusicViewController ()<AVAudioPlayerDelegate>
- (IBAction)jump:(UIBarButtonItem *)sender;
/**
 *  存放所有的音乐模型数据
 */
@property(nonatomic,strong) NSArray *musics;

@property(nonatomic,strong)CADisplayLink *link;
/**
 *  当前正在播放的音乐播放器
 */
@property (nonatomic, strong) AVAudioPlayer *currentPlayingAudioPlayer;
@end

@implementation MusicViewController

/**
 *  懒加载 创建音乐模型数据
 */
- (NSArray *)musics
{
    if (!_musics) {
        NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Musics.plist" ofType:nil]];
        
        NSMutableArray *arrayM = [NSMutableArray array];
        for (NSDictionary *dict in array) {
            [arrayM addObject:[MusicModel musicWithDict:dict]];
        }
        _musics = arrayM;
    }
    return _musics;
}

- (CADisplayLink *)link
{
    if (!_link) {
        self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
    }
    return _link;
}

/**
 *  实时更新(1秒中调用60次)
 */
- (void)update
{
    //监听播放进度
    //总时长、当前时间
//    NSLog(@"%f %f", self.currentPlayingAudioPlayer.duration, self.currentPlayingAudioPlayer.currentTime);
    
#warning 调整歌词
}

- (void)viewDidLoad {
    [super viewDidLoad];
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.musics.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //1.创建cell
    MusicCell *cell = [MusicCell cellWithTableView:tableView];
    
    //2.设置cell的数据
    cell.model = self.musics[indexPath.row];
    
    //3.
    return cell;
}

#pragma mark -Table view delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 70;
}

/**
 *  取消选中会调用
 */
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //停止播放
    MusicModel *model = self.musics[indexPath.row];
    [AudioTool stopMusic:model.filename];
    
    //再次传递模型(让上一手歌的图片停止转动)
    MusicCell *cell = (MusicCell *)[tableView cellForRowAtIndexPath:indexPath];
    model.playing = NO;
    cell.model = model;
}

/**
 *  点击了cell(选中了)
 */
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //播放音乐
    MusicModel *model = self.musics[indexPath.row];
    AVAudioPlayer * audioPlayer = [AudioTool playMusic:model.filename];
    audioPlayer.delegate = self;
    self.currentPlayingAudioPlayer = audioPlayer;
    
    //在锁屏界面显示歌曲信息
    [self showInfoInLockedScreen:model];
    
    //开启定时器监听播放进度
    [self.link invalidate];
    self.link = nil;
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    //再次传递模型(让cell上图片转动起来)
    MusicCell *cell = (MusicCell *)[tableView cellForRowAtIndexPath:indexPath];
    model.playing = YES;
    cell.model = model;
}

/**
 *  在锁屏界面显示歌曲信息
 */
- (void)showInfoInLockedScreen:(MusicModel *)model
{
    NSMutableDictionary *info = [NSMutableDictionary dictionary];
    // 标题(音乐名称)
    info[MPMediaItemPropertyTitle] = model.name;
    
    // 作者
    info[MPMediaItemPropertyArtist] = model.singer;
    
    // 专辑
    info[MPMediaItemPropertyAlbumTitle] = model.singer;
    
    // 图片
    info[MPMediaItemPropertyArtwork] = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:model.icon]];
    
    [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = info;
}

#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    //计算下一行
    NSIndexPath *selectedPath = [self.tableView indexPathForSelectedRow];
    long next = selectedPath.row + 1;
    if (next == self.musics.count) {
        next = 0;
    }
    
    //停止上一首歌的转圈
    MusicCell *cell = (MusicCell *)[self.tableView cellForRowAtIndexPath:selectedPath];
    MusicModel *model = self.musics[selectedPath.row];
    model.playing = NO;
    cell.model = model;
    
    //播放下一首歌
    NSIndexPath *currentPath = [NSIndexPath indexPathForRow:next inSection:selectedPath.section];
    [self.tableView selectRowAtIndexPath:currentPath animated:YES scrollPosition:UITableViewScrollPositionTop];
    [self tableView:self.tableView didSelectRowAtIndexPath:currentPath];
}

/**
 *  音乐播放器被打断(电话)
 */
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{}

/**
 *  打断结束(电话结束)
 */
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{
    [player play];
}

- (IBAction)jump:(UIBarButtonItem *)sender
{
    //设置当前播放器的当前时间为 总时长的 最后3秒
    self.currentPlayingAudioPlayer.currentTime = self.currentPlayingAudioPlayer.duration - 3;
}
@end

4.自定义的UITableViewCell

//
//  MusicCell.h


#import <UIKit/UIKit.h>

@class MusicModel;
@interface MusicCell : UITableViewCell
+ (instancetype)cellWithTableView:(UITableView *)tableView;

@property (nonatomic, strong) MusicModel *model;
@end
//
//  MusicCell.m


#import "MusicCell.h"
#import "MusicModel.h"
#import "UIImage+MJ.h"
#import "Colours.h"

@interface MusicCell()
@property (nonatomic, strong) CADisplayLink *link;
@end

@implementation MusicCell

- (CADisplayLink *)link
{
    if (!_link) {
        self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
    }
    return _link;
}

+ (instancetype)cellWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"music";
    MusicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[MusicCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }
    return cell;
}

- (void)setModel:(MusicModel *)model
{
    _model = model;
    
    self.textLabel.text = model.name;
    self.detailTextLabel.text = model.singer;
    self.imageView.image = [UIImage circleImageWithName:model.singerIcon borderWidth:2.0 borderColor:[UIColor steelBlueColor]];
   
    if (model.isPlaying) {
        //转圈
        [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    }else{ //停止动画
        [self.link invalidate];
        self.link = nil;
        [UIView animateWithDuration:2.0 animations:^{
            self.imageView.transform = CGAffineTransformIdentity;
        }];
    }
}

/**
 *  8秒转一圈, 45°/s
 */
- (void)update
{
    // 1/60秒 * 45
    // 规定时间内转动的角度 == 时间 * 速度
    CGFloat angle = self.link.duration * M_PI_4;
    self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, angle);
}

@end

5.用到的其他

//
//  UIImage+MJ.h


#import <UIKit/UIKit.h>

@interface UIImage (MJ)
/**
 *  返回一个带边框的圆形图片
 *
 *  @param name        图片名称
 *  @param borderWidth 边框宽度
 *  @param borderColor 边框颜色
 */
+ (instancetype)circleImageWithName:(NSString *)name borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor;
@end
//
//  UIImage+MJ.m


#import "UIImage+MJ.h"

@implementation UIImage (MJ)
+ (instancetype)circleImageWithName:(NSString *)name borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor
{
    // 1.加载原图
    UIImage *oldImage = [UIImage imageNamed:name];
    
    // 2.开启上下文
    CGFloat imageW = oldImage.size.width + 2 * borderWidth;
    CGFloat imageH = oldImage.size.height + 2 * borderWidth;
    CGSize imageSize = CGSizeMake(imageW, imageH);
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
    
    // 3.取得当前的上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    // 4.画边框(大圆)
    [borderColor set];
    CGFloat bigRadius = imageW * 0.5; // 大圆半径
    CGFloat centerX = bigRadius; // 圆心
    CGFloat centerY = bigRadius;
    CGContextAddArc(ctx, centerX, centerY, bigRadius, 0, M_PI * 2, 0);
    CGContextFillPath(ctx); // 画圆
    
    // 5.小圆
    CGFloat smallRadius = bigRadius - borderWidth;
    CGContextAddArc(ctx, centerX, centerY, smallRadius, 0, M_PI * 2, 0);
    // 裁剪(后面画的东西才会受裁剪的影响)
    CGContextClip(ctx);
    
    // 6.画图
    [oldImage drawInRect:CGRectMake(borderWidth, borderWidth, oldImage.size.width, oldImage.size.height)];
    
    // 7.取图
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    // 8.结束上下文
    UIGraphicsEndImageContext();
    
    return newImage;
}
@end

还用到了Colours.h,非必要。如需要可去github下载。




相关文章推荐

android音乐播放器_歌曲列表

歌曲列表是来显示SD卡或手机内存中的歌曲,因为android会自动扫描媒体对象,直接使用MediaStore就可以显示歌曲名称、艺术家、缩略图等。再次使用ListView来显示这些信息,xml布局很简...

android音乐播放器开发 SweetMusicPlayer 加载歌曲列表

android音乐播放器开发   SweetMusicPlayer   加载歌曲列表 上一篇写了播放器的整体实现思路, http://blog.csdn.net/huweigoodboy/arti...

Android开发本地及网络Mp3音乐播放器(十二)创建NetMusicListAdapter、SearchResult显示网络音乐列表

实现功能: 实现NetMusicListAdapter(网络音乐列表适配器) 实现SearchResult(搜索音乐对象) 使用Jsoup组件请求网络,并解析音乐数据,并,音乐数据加载到列表中 实现F...

自己动手做Android音乐播放器

软硬件环境 Macbook Pro MGX 72 Android Studio 1.3.1 红米Note增强版 前言市面上的android音乐播放器很多,有的还做得很大,像QQ音乐、酷狗音乐等。今天我...

手把手叫你制作一个精美的在线音乐播放器

最近项目中要增加一些特殊的功能,实现音乐的在线播放。虽说网上源码一大把,demo一大堆,但是能用的其实寥寥无几抓狂,看来关键时刻还是自己动手,丰衣足食啊。...

仿酷狗音乐播放器开发日志二十一 开发动态调色板控件(附源码)

上一篇仿酷狗日志结束后,整个换肤功能就只剩下调色板功能没有做了,我本以为会很简单,但是研究了酷狗的调色板功能后发现不是那么简单的事情。酷狗的调色板是可以动态变化的,QQ的也是。这篇日志记录如何开发一个...

Android开源项目-Jamendo音乐播放器研究与优化-基于Builder模式的数据库操作

【一个音符的时值按二等分的原则,成偶数细分下去,称为音符时值的基本划分】 --- 《五线谱基础教程》 对于一个完整的应用来说,数据库操作往往是避免不了的,Jamendo基于生成器模式(...
  • ACE1985
  • ACE1985
  • 2012年09月01日 19:26
  • 2759

推荐5 款好用的 Linux 音乐播放器

目前 Linux 上有几十个音乐播放器,这使得找到一个最好用的变成很困难。之前我们已经回顾了其中的一些播放器,如 Cantata,Exaile,甚至不那么出名的 Clementine,Nighting...

Android 音乐播放器的开发教程(十一)SQLite的使用及最近播放的实现 ----- 小达

SQLite的使用及最近播放的实现

android音乐播放器的音频焦点控制

什么叫音频焦点 (audio focus)?   android系统是一个多任务操作系统,因此同一时刻允许许多任务同时工作。但是这对音频类应用来说是个挑战,因为如果多个音频同时播放的话,很多情况...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:音乐播放器-小案列
举报原因:
原因补充:

(最多只允许输入30个字)