

MasterViewController.h
//
// MasterViewController.h
// MP3Player
//
// Created by Lab10 on 13. 8. 6..
// Copyright (c) 2013년 Lab10. All rights reserved.
//
#import <UIKit/UIKit.h>
// 헤더 추가
//#import "AVFoundation/AVAudioPlayer.h"
//#define AUDIO_PLAYER_EXAM // AVAudioPlayer 기능 테스트
@class DetailViewController;
@interface MasterViewController : UITableViewController
{
// 오디오 재생 클래스
//AVAudioPlayer *audioPlayer;
// mp3 리스트
NSArray *dirContents;
}
@property (strong, nonatomic) DetailViewController *detailViewController;
@end
MasterViewController.m
//
// MasterViewController.m
// MP3Player
//
// Created by Lab10 on 13. 8. 6..
// Copyright (c) 2013년 Lab10. All rights reserved.
//
#import "MasterViewController.h"
#import "DetailViewController.h"
// 헤더 추가
#import "MP3ViewController.h"
@interface MasterViewController () {
NSMutableArray *_objects;
}
@end
@implementation MasterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//self.title = NSLocalizedString(@"Master", @"Master");
self.title = @"MP3 Player";
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
#ifdef AUDIO_PLAYER_EXAM
// 번들 mp3파일 경로 생성
NSString *mp3FilePath=[[NSBundle mainBundle] pathForResource:@"tellmetellme" ofType:@"mp3"];
// NSURL 형식으로 변환
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:mp3FilePath];
// 오디오 플레이어 생성
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
// 사운드 재생 준비 확인
[audioPlayer prepareToPlay];
#endif
// 상태 바 스타일 변경
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
// 네비게이션 바 스타일 변경
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
// 도큐먼트 디렉터리에서 mp3 파일을 획득하여 리스트 생성
NSArray *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [docPath objectAtIndex:0];
NSArray *extensions = [NSArray arrayWithObjects:@"mp3", @"MP3", nil];
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docDir error:nil];
dirContents = [contents filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"pathExtension IN %@", extensions]];
// self.navigationItem.leftBarButtonItem = self.editButtonItem;
// UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
// self.navigationItem.rightBarButtonItem = addButton;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
//#pragma mark - Table View
//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
//{
// return 1;
// return [dirContents count];
//}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//return _objects.count;
return [dirContents count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// NSDate *object = _objects[indexPath.row];
// cell.textLabel.text = [object description];
#ifdef AUDIO_PLAYER_EXAM
if(audioPlayer.isPlaying)
{
// 재생 중이면 [일시정지]를 표시
cell.textLabel.text = @"PAUSE";
}
else
{
// 재생 중이 아니면 [재생]을 표시
cell.textLabel.text = @"PLAY";
}
#endif
cell.textLabel.text = [dirContents objectAtIndex:indexPath.row];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_objects removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
#ifdef AUDIO_PLAYER_EXAM
if(audioPlayer.isPlaying)
{
// 재생 중이면 일시정지
[audioPlayer pause];
}
else
{
// 재생 중이 아니면 재생 시작
[audioPlayer play];
}
// 테이블 뷰 갱신
[self.tableView reloadData];
#endif
// MP3ViewController 생성 및 삽입
MP3ViewController *subViewController = [[MP3ViewController alloc] initWithNibName:@"MP3ViewController" bundle:nil];
[subViewController setFileList:dirContents fileIndex:indexPath.row];
[self.navigationController pushViewController:subViewController animated:YES];
}
@end
DetailViewController.h
//
// DetailViewController.h
// MP3Player
//
// Created by Lab10 on 13. 8. 6..
// Copyright (c) 2013년 Lab10. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@end
DetailViewController.m
//
// DetailViewController.m
// MP3Player
//
// Created by Lab10 on 13. 8. 6..
// Copyright (c) 2013년 Lab10. All rights reserved.
//
#import "DetailViewController.h"
@interface DetailViewController ()
- (void)configureView;
@end
@implementation DetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(@"Detail", @"Detail");
}
return self;
}
@end
MP3ViewController.h
//
// MP3ViewController.h
// MP3Player
//
// Created by Lab10 on 13. 8. 6..
// Copyright (c) 2013년 Lab10. All rights reserved.
//
#import <UIKit/UIKit.h>
// 헤더 추가
#import "AVFoundation/AVAudioPlayer.h"
#import "MediaPlayer/MPVolumeView.h"
// 메타 데이터 관련 헤더
#import "AVFoundation/AVAsset.h"
#import "AVFoundation/AVMetadataItem.h"
// 재생 상태 값 열거
typedef enum PLAY_STATE{
NOT_READY = 0, // 준비되지 않음
PLAYING, // 재생
PAUSED, // 일시정지
STOPPED, // 정지
}_PLAY_STATE;
@interface MP3ViewController : UIViewController
{
// 오디오 재생 클래스
AVAudioPlayer *audioPlayer;
// 파일 리스트
NSArray *fileList;
// 파일 인덱스
NSInteger fileIndex;
// 타임라인 슬라이더
UISlider *timeSlider;
// 볼륨 뷰 컨트롤
MPVolumeView *volumeView;
// 현재 시간
UILabel *curTime;
// 총 재생 시간
UILabel *totTime;
// 재생 버튼
UIButton *btnPlay;
// 파일 이름
NSString *fileName;
// 컨트롤 배경
UIImageView *ctrlBg;
// 슬라이더 배경
UIImageView *progBg;
// 이전 버튼
UIButton *btnPrev;
// 일시정지 버튼
UIButton *btnPause;
// 다음 버튼
UIButton *btnNext;
// 재킷 이미지
UIImageView *artworkView;
// 재생 상태
int playState;
// 오버레이 상태
BOOL bOverlay;
// 드래깅 상태
BOOL bDragging;
// 타이머
NSTimer *tsTimer;
}
// 파일 리스트 설정
- (void) setFileList:(NSArray *)list fileIndex:(NSInteger) index;
@end
MP3ViewController.m
//
// MP3ViewController.m
// MP3Player
//
// Created by Lab10 on 13. 8. 6..
// Copyright (c) 2013년 Lab10. All rights reserved.
//
#import "MP3ViewController.h"
@interface MP3ViewController ()
@end
@implementation MP3ViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// 타이틀을 파일명으로 변경
self.title = [fileList objectAtIndex:fileIndex];
// 파일 URL 생성
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", docDirectory, [fileList objectAtIndex:fileIndex]];
// 재킷 이미지 출력
[self artworksForFileAtPath:filePath];
// 플레이어 컨트롤 초기화
[self initPlayerControls];
// 오디오 플레이어 생성
NSURL *url = [NSURL fileURLWithPath:filePath];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
audioPlayer.delegate = (id)self;
if(audioPlayer == nil)
{
return;
}
else
{
if([audioPlayer prepareToPlay])
{
// 탐색 슬라이더 설정
[timeSlider setMaximumValue:[audioPlayer duration]];
// 현재 시간 업데이트 타이머 시작
tsTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateTimeStamp) userInfo:nil repeats:YES];
// 재생 시작
[audioPlayer play];
}
}
}
// 뷰가 사라질 때 처리를 위해서 추가
- (void) viewWillDisappear:(BOOL)animated
{
// 재생 중인 경우 정지
if(audioPlayer.isPlaying)
{
[audioPlayer stop];
}
[super viewWillDisappear:animated];
}
// 재생 종료 델리게이트 매서드
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// 다음 곡을 재생
[self onBtnNext];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// 오디오 파일 리스트 및 인덱스 설정
- (void) setFileList:(NSArray *)list fileIndex:(NSInteger)index
{
// 파일 리스트 저장
fileList = list;
// 현재 파일 인덱스 저장
fileIndex = index;
}
// MP3 파일에서 Artwork 이미지 추출
- (void)artworksForFileAtPath:(NSString *)path
{
if (!artworkView)
{
// 재킷 이미지 출력을 위한 이미지 뷰를 생성
artworkView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
artworkView.userInteractionEnabled = YES;
[self.view addSubview:artworkView];
// 탭 제스처를 등록
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(overlaySwitch)];
[tap setNumberOfTapsRequired:1];
[artworkView addGestureRecognizer:tap];
}
// 파일의 메타데이터를 획득
AVAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
NSArray *metadata = [asset commonMetadata];
// artwork 데이터를 artworkView에 적용
for (AVMetadataItem* item in metadata)
{
if([[item commonKey] isEqualToString:@"artwork"])
{
UIImage *artworkImage = nil;
if([[item value] isKindOfClass:[NSDictionary class]])
{
NSDictionary *imageDic = (NSDictionary *) [item value];
if([imageDic objectForKey:@"data"] != nil)
{
NSData *imageData = [imageDic objectForKey:@"data"];
artworkImage = [UIImage imageWithData:imageData];
artworkView.image = artworkImage;
}
}
}
}
}
// 오디오 파일 다시 재생
- (void) restartPlayer
{
// 타이틀 변경
self.title = [fileList objectAtIndex:fileIndex];
// 파일 URL 생성
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", docDirectory, [fileList objectAtIndex:fileIndex]];
NSURL *url = [NSURL fileURLWithPath:filePath];
// 오디오 플레이어 생성 및 적용
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
player.delegate = (id)self;
audioPlayer = player;
// 컨트롤 세팅 및 재생 시작
if([audioPlayer prepareToPlay])
{
// 탐색 범위 설정
[timeSlider setMaximumValue:[audioPlayer duration]];
// 타이머 리셋
[tsTimer invalidate];
tsTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateTimeStamp) userInfo:nil repeats:YES];
// 재킷 이미지 변경
[self artworksForFileAtPath:filePath];
// 재생 시작
[audioPlayer play];
}
}
// 이전 버튼 이벤트
- (void) onBtnPrev
{
if(fileIndex == 0)
{
fileIndex = [fileList count]-1;
}
else
{
fileIndex--;
}
btnPlay.hidden = YES;
btnPause.hidden = NO;
[self restartPlayer];
}
// 다음 버튼 이벤트
- (void) onBtnNext
{
if([fileList count] <= fileIndex+1)
{
fileIndex = 0;
}
else
{
fileIndex++;
}
btnPlay.hidden = YES;
btnPause.hidden = NO;
[self restartPlayer];
}
// 재생 버튼 이벤트
- (void) onBtnPlay
{
// 상태 변경 및 재생 시작
playState = PLAYING;
btnPlay.hidden = YES;
btnPause.hidden = NO;
[audioPlayer play];
}
// 일시정지 버튼 이벤트
- (void) onBtnPause
{
// 상태 변경 및 일시정지
playState = PAUSED;
btnPlay.hidden = NO;
btnPause.hidden = YES;
[audioPlayer pause];
}
// 오버레이 뷰 보이기/숨기기
- (void) showOverlay:(BOOL) show
{
progBg.hidden = timeSlider.hidden = curTime.hidden = totTime.hidden = !show;
}
// 오버레이 뷰 상태 변경
- (void) overlaySwitch
{
// 오버레이 플래그를 변경
bOverlay = !bOverlay;
[self showOverlay:bOverlay];
}
// 재생시간 업데이트
- (void) updateTimeStamp
{
// 플레이어가 생성되지 않은 경우 타이머 종료
if(audioPlayer == nil)
{
[tsTimer invalidate];
tsTimer = nil;
return;
}
// 드레깅 중에는 취소
if(bDragging) return;
// 현재 시간, 남은 시간 계산
int cur = (int)[audioPlayer currentTime];
int ext = (int)[audioPlayer duration]-cur;
// 슬라이더 위치 변경
[timeSlider setValue:[audioPlayer currentTime]];
// 시간 업데이트
[curTime setText:[NSString stringWithFormat:@"%02d:%02d", cur/60, cur%60]];
[totTime setText:[NSString stringWithFormat:@"-%02d:%02d", ext/60, ext%60]];
}
// 타임라인 드래그 처리
- (void) slideDragging
{
// 현재 시간, 남은 시간 계산
int cur = (int)timeSlider.value;
int ext = (int)timeSlider.maximumValue-cur;
// 시간 업데이트
if(audioPlayer != nil)
{
[curTime setText:[NSString stringWithFormat:@"%02d:%02d", cur/60, cur%60]];
[totTime setText:[NSString stringWithFormat:@"-%02d:%02d", ext/60, ext%60]];
}
}
// 슬라이드 선택
- (void) slideSelected
{
// 플래그 변경
bDragging = YES;
}
// 슬라이드 해제
- (void) slideRelease
{
// 플래그 변경
bDragging = NO;
// 재생 위치를 변경
[audioPlayer setCurrentTime:timeSlider.value];
}
// 플레이어 컨트롤 초기화
- (void) initPlayerControls
{
// 아래쪽 바 생성
ctrlBg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"bottom_bar.png"]];
ctrlBg.frame = CGRectMake(0, 320, 320, 96);
[self.view addSubview:ctrlBg];
// 탐색 슬라이더 배경
progBg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"progress_bg.png"]];
progBg.frame = CGRectMake(0, 0, 320, 44);
progBg.alpha = 0.9f;
[self.view addSubview:progBg];
// 탐색 슬라이더
timeSlider = [[UISlider alloc] initWithFrame:CGRectMake(50, 12, 220, 22)];
[self.view addSubview:timeSlider];
[timeSlider addTarget:self action:@selector(slideDragging) forControlEvents:UIControlEventTouchDragInside];
[timeSlider addTarget:self action:@selector(slideSelected) forControlEvents:UIControlEventTouchDown];
[timeSlider addTarget:self action:@selector(slideRelease) forControlEvents:UIControlEventTouchUpInside];
[timeSlider addTarget:self action:@selector(slideRelease) forControlEvents:UIControlEventTouchUpOutside];
// 현재 시간, 남은 시간 출력 레이블
curTime = [[UILabel alloc] initWithFrame:CGRectMake(10, 12, 35, 22)];
curTime.text = @"00:00";
totTime = [[UILabel alloc] initWithFrame:CGRectMake(275, 12, 40, 22)];
totTime.text = @"-00:00";
curTime.backgroundColor = totTime.backgroundColor = [UIColor clearColor];
curTime.textColor = totTime.textColor = [UIColor whiteColor];
curTime.font = totTime.font = [UIFont boldSystemFontOfSize:12];
// curTime.textAlignment = UITextAlignmentRight;
[self.view addSubview:curTime];
[self.view addSubview:totTime];
// 이전 곡 버튼
btnPrev = [[UIButton alloc] init];
btnPrev.frame = CGRectMake(64, 325, 44, 44);
[btnPrev setImage:[UIImage imageNamed:@"button_prev.png"] forState:UIControlStateNormal];
[btnPrev addTarget:self action:@selector(onBtnPrev) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnPrev];
// 재생시작 버튼
btnPlay = [[UIButton alloc] init];
btnPlay.frame = CGRectMake(138, 325, 44, 44);
[btnPlay setImage:[UIImage imageNamed:@"button_play.png"] forState:UIControlStateNormal];
[btnPlay addTarget:self action:@selector(onBtnPlay) forControlEvents:UIControlEventTouchUpInside];
btnPlay.hidden = YES;
[self.view addSubview:btnPlay];
// 일시정지 버튼
btnPause = [[UIButton alloc] init];
btnPause.frame = CGRectMake(138, 325, 44, 44);
[btnPause setImage:[UIImage imageNamed:@"button_pause.png"] forState:UIControlStateNormal];
[btnPause addTarget:self action:@selector(onBtnPause) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnPause];
// 다음 곡 버튼
btnNext = [[UIButton alloc] init];
btnNext.frame = CGRectMake(212, 325, 44, 44);
[btnNext setImage:[UIImage imageNamed:@"button_next.png"] forState:UIControlStateNormal];
[btnNext addTarget:self action:@selector(onBtnNext) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnNext];
// 볼륨 조절 컨트롤
volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(30, 370, 260, 46)];
[volumeView sizeToFit];
[self.view addSubview:volumeView];
// 오버레이 컨트롤 감춤
[self showOverlay:bOverlay];
}
@end