posted by 프띠버리 2013. 8. 21. 17:40





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