아이폰 어플 개발기 - Chapter 3. Play Sound & Music, http://bit.ly/9SBB0p
by 박주일(@KeatonY)


목차입니다.

1. Animate Image by UIImageView
2. Touch Event Handling
3. Play Sound & Music
4. Multi View Handling
5. Play Intro Movie
6. Game Logic (include RadomizeShuffleQueue)


언능 3편 연재합니다. ^^

일 단 지난번까지 내용을 살펴보면
화면 한가운데 있는 이미지 표시하고 그 이미지 영역을 터치하면 이미지가 애니메이션되는 작업까지 처리했습니다.

이미지만 움직이는 상태가 되니 뭔가 좀 더 하고 싶어지죠.
터치했을때 이미지가 움직이고...아~! 소리를 나게 해보고 싶네요.

MP3나 WAV 파일을 준비해두고
처음 시작하면 MP3를 배경음악으로 플레이하고
이미지를 클릭하면 WAV 파일을 효과음으로 플레이하고 싶습니다.
(둘 다 mp3나 wav 를 사용하셔도 괜찮습니다만...
일반적으로 배경 음악에는 처음 로딩할때는 시간이 걸리더라도 용량이 적은 mp3로..
효과음 에는 플레이하는데 로딩하는데 딜레이가 적고 본래 음에 가까운 wav 를 사용하게 됩니다.)

1. Play Sound & Music

WAV 파일을 플레이하던 MP3 파일을 플레이던 다르지 않습니다.

기 본 코드를 찾아보면

NSURL *url = [NSURL fileURLWithPath:@"%@/myBGM.mp3", [[NSBundle mainBundle] resourcePath]];
NSError *error;
    
playerBGM = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
[playerBGM setNumberOfLoops:-1];
    
if (playerBGM == nil)
    NSLog([error description]);
else
    [playerBGM play];


이 정도의 예제 코드를 찾아보실 수 있습니다.

일단 코드를 살펴보면 myBGM.mp3 이라는 음악 파일을 플레이하는 코드입니다.
setNumberOfLoops 에 -1 이라고 써있는걸 보니 무한 반복할때는 저렇게 하는것 같습니다.

playerBGM 이 제대로 생성되지 않아서 nil 상태이면 로그로 남겨주는 코드도 있네요.
그리고나서 정상적으로 playerBGM 객체가 생성되었다면 (!= nil 이라면) playerBGM 을 play 하게 됩니다. 그리고 무한 반복하는 것이죠.

간단하 네요.

근데 위 코드를 사용하기 위해서는 좀 더 해야할 작업들이 남아있습니다.
일단 AVAudioPlayer 로 선언된 playerBGM 이 필요합니다. (뭐 제가 이름지은 변수이니 변수명은 편한대로 정하시면 됩니다.)

먼저 .h 에 AVAudioPlayer 를 사용하기 위한 기본 선언을 추가합니다.

#import <UIKit/UIKit.h> 밑에
#import <AVFoundation/AVFoundation.h>
를 추가합니다.

@interface XXXViewController : UIViewController
{
AVAudioPlayer *playBGM;
...
}

이렇게 변수를 선언합니다.

이제 .m 으로 돌아가서
- (void)viewDidUnload {
    playBGM = nil;
    ...
}
- (void)dealloc {
    [playBGM release];
    ...
    [super dealloc];
}


메모리 해제하는 코드부터 넣어주고 시작합니다.

일단 현재 뷰가 로딩되면 준비된 배경음악을 플레이하고 싶습니다.

그럼 viewDidLoad 부분에 넣으면 되겠죠.

-(void)viewDidLoad
{
    NSURL *url = [NSURL fileURLWithPath:@"%@/myBGM.mp3", [[NSBundle mainBundle] resourcePath]];
    NSError *error;
    
    playerBGM = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    [playerBGM setNumberOfLoops:-1];
    
    if (playerBGM == nil)
        NSLog([error description]);
    else
        [playerBGM play];
}


이 활용은 다양한 방법으로 하시면 됩니다.

wav 파일을 플레이하고 싶으면 파일명만 바꾸면 되고
터치시 플레이하고 싶으면 touchesEnded 안에 입력하면 됩니다.

2. Framework 추가하기


위 설명대로 하려고 하다보니 문제가 생기네요.

일단 첫번째 문제...

컴파일하려니 에러가 나옵니다.

#import <AVFoundation/AVFoundation.h>

이 부분에서 말이죠.

설명을 찾아보니 Frameworks 에서 AVFoundation.framework 를 찾아서 추가해줘야 한다고 합니다.
관련된 라이브러리를 추가해야 한다는 거네요.

추가해보죠 뭐..

Frameworks 탭을 보니 기본으로 UIKit, Foundation, CoreGraphics 가 포함되어 있네요.
우클릭 -> Add -> Existing Frameworks...
선택했더니 Finder 가 뜨는데 여기저기 아무리 뒤져봐도 AVFoundation.framework가 없어요.
비슷해보이는 Framework를 찾아서 추가시켜봤는데 영 안되는군요.
저 이부분에서 한번 낑낑거리다 포기했었습니다.
책을 아무리 뒤져봐도 AVFoundation.framework 찾아서 추가시키라고만 되어있는데 시키는대로 안되니 말이죠.
그래서 여기저기 구글님한테도 물어보고 하다가 겨우 해결했습니다.

자 문제 해결~
Targets 탭을 엽니다. 프로젝트 명으로 된 이름이 보입니다.
여기서 우클릭->Get Info 선택
창이 뜹니다. 하단에 보니 Linked Libraries 가 있네요. 위와 마찬가지로 UIKit, Foundation, CoreGraphics 만 달랑 있습니다.
좌측 하단에 + 버튼을 클릭합니다.
AVFoundation.framework 가 제일 위에 있네요.
Add 를 선택해줍니다.
원했던 AVFoundation.framework 가 제대로 추가되었습니다.
컴파일 해보니 문제가 정상적으로 해결되었습니다.

아 이거 왜 이렇게 불편하게 만들었을까요?
직 관적이지 않습니다.
이 문제로 고민하신 분이 저뿐만은 아니라는 확신에 이렇게 개발기에 남깁니다.

제가 아는 분 하나도 이거 가지고 낑낑대고 있는걸 이런 간단한 팁으로 도움을 준 기억이 있습니다.
이거 의외로 제대로 설명해주는데가 별로 없더군요.

도움 되셨길 바랍니다.

3. 메모리 문제?

위에서는 viewDidLoad 부분에 배경음악 플레이 하는 코드를 넣었습니다.
유사하게 터치 이벤트시마다 준비된 wav 파일을 플레이하고 싶습니다.

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    ...
    playerVoice = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];

    ...
}


근데 해놓고 보니 약간 불안해집니다.
메모리 해제는 dealloc 부분에 한번 해주는데 touchesEnded 는 터치 이벤트가 있을때마다 불리니까요.

사실 메모리 문제는 문제가 생기고 나서나 알게되지만 위의 방법에는 그것보다 직접적인 문제가 발생합니다.

예를 들어 터치하게되면 "안녕하세요~" 라는 내용의 hello.wav 파일을 플레이 한다고 하면

한번 터치하고 마는 경우에는 "안녕하세요~" 이렇게 제대로 플레이 되고 말겠지만
터치를 마구 하는 사용자를 만나면 어떻게 될까요?

"안!!안!안!안!안!안!안!안!안!안!안!안!안녕하세요~" 를 들으실수 있을겁니다. ^^
부끄럽지만 경험담입니다.

메모리 문제와 계속 플레이되는 문제를 한꺼번에 해결하기 위해서는
위 코드가 호출될때마다 초기화 하는 코드를 넣어주면 됩니다.

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    if (playerVoice != nil)
    {
        [playerVoice stop];
        [playerVoice release];
        playerVoice = nil;
    }
    ...
    NSURL *url = [NSURL fileURLWithPath:@"%@/myVoice.wav", [[NSBundle mainBundle] resourcePath]];
    NSError *error;
    
    playerVoice = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    [playerVoice setNumberOfLoops:0];
    
    if (playerVoice == nil)
        NSLog([error description]);
    else
        [playerVoice play];
    ...
}


이렇게 이미 playerVoice 가 생성되서 플레이 중이었다면 플레이중인 것을 스탑 STOP 해주고, 메모리에서 해제 RELEASE 해주는 코드를 추가해주면 됩니다.

아까 mp3 플레이 할때랑 달라진건 setNumberOfLoops 에 0을 넣어준 것이죠.
이렇게 해주면 딱 한번만 플레이되게 됩니다.
플레이 횟수를 적어주는 것이 아니라 반복 횟수를 적어주는 것이니 헷갈리지 않으시길 바랍니다. ^^


아 이거 쭈욱 써놓고 보니 전 정말 간단한 얘기 길게 늘려서 쓰는데 재주가 있는가 봅니다.

이 긴 내용 읽고나서 남는게 별로 없어서 아쉬워하실 분도 많이 있을것 같네요.

그래도 작은 경험담이라도 공유해서 도움되는 분이 계시길 바라면서 이만 정리해봅니다.







이 게시물을..