안녕하세요~ 두결입니다.
지난시간에는 체크박스를 화면에 표시하고 체크박스의 선택된 값을 가져오는 방법과
Radio버튼 선택시, CheckBox선택 완료 시 프로그래스바의 진행율을 설정하는 방법에 대해서 알아보았습니다.
이번시간부터는 사운드 부분에 대해서 알아보도록 하겠습니다.
▣ SoundPool을 사용하여 간단한 효과음 재생하기
먼저, 새프로젝트를 만들어보겠습니다.
프로젝트 명은 SoundTest라고 하겠습니다.
앱상에서 간단한 소리를 재생할수 있게 해주는 것이 바로 SoundPool입니다.
SoundPool은 길이가 짧은 효과음을 빠르고 효율적으로 재생하기 위해 안드로이드에서 제공하는 내장 라이브러리입니다.
먼저, 소리를 내려면 사운드파일이 있어야겠죠?
간단한 사운드파일은 wav파일인데 wav파일은 인터넷에서 받으셔도 되고 내컴퓨터에 wav파일 검색하면 많이 나오실거에요..

저는 이렇게 2개의 파일을 준비하였습니다.
OK 소리하고 NG소리인데요...
ImageView에 이미지 넣을때 처럼 복사해서 프로젝트에 붙여넣기 하면 되는데요..
이미지는
app->res->drawable 폴더에 붙여넣기하였습니다.
하지만, 이 사운드파일은
app->res->raw 폴더 밑에 붙여넣기 해야합니다.

하지만, 지금 현재 프로젝트에는 raw폴더는 안보이네요...
처음에는 raw폴더가 없지만 괜찮습니다. 새로 만들면 됩니다.

app->res 에서 오른쪽버튼
New->Android Resource Directory 선택하시구요~

Resource types:에서 raw를 선택하고 OK!!

그러면, 위와같이 raw폴더가 생성됩니다.
이제, 준비해둔 2개의 사운드파일을 복사하고
raw폴더에 붙여넣기 하겠습니다.

그러면 위와같이 2개의 wav파일이 리소스에 잘 등록이 되었습니다. ^^
자, 이제 버튼을 2개 만들어서 버튼을 누를때 효과음이 나도록 해보겠습니다.

위와같이 버튼 2개를 갖다놓구요~
<Button
android:id="@+id/btnOKSound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK 효과음"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
<Button
android:id="@+id/btnNGSound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NG 효과음"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.8"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
XML코드에서
ID는 btnOKSound, btnNGSound라고 하고
text는 "OK 효과음", "NG 효과음"이라고 하였습니다.
Button btn_OKSound = findViewById(R.id.btnOKSound);
btn_OKSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
Button btn_NGSound = findViewById(R.id.btnNGSound);
btn_NGSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
MainActivity.java에서
버튼 2개 객체 생성하고 Click이벤트까지 작성하였습니다.
한번 실행을 해보면

네, 버튼 2개가 잘 보이네요..^^
자, 이제 여기에 SoundPool을 이용하여 소리를 내는 코드만 추가하면 됩니다.
SoundPool soundPool = new SoundPool.Builder().build();
nSoundID = soundPool.load(getApplicationContext(),R.raw.oksound,1);
Button btn_OKSound = findViewById(R.id.btnOKSound);
btn_OKSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
soundPool.play(nSoundID, 1f, 1f, 1, 0, 1f);
}
});
위의 코드가 SoundPool을 이용하여 "oksound.wav" 사운드 파일을 재생하는 기본적인 코드입니다.
SoundPool.Builder().build()를 통해 SoundPool객체를 생성하고
SoundPool.load()함수를 통해 재생하고자하는 사운드파일을 불러옵니다.
SoundPool.load()는 짧은 음성파일을 메모리에 로드하여 재생을 준비하는 함수입니다.

SoundPool.load()함수의
첫번째 인자는 context라서 getapplicationContext()로 해주었고
두번째 인자는 resID, 출력하고자하는 사운드파일 ID로 R.raw.oksound로 하였습니다.
세번째 인자는 priority인데 시스템 리소스가 부족할때 어떤 사운드를 먼저 처리할지를 결정하는데 값이 작을수록 우선순위가 높습니다. 그래서 1로 하였습니다.
그리고, 이 함수의 리턴값이 Load된 사운드 ID를 반환하고 나중에 SoundPool.play() 함수를 사용할때 이 ID를 이용합니다.
마지막으로 버튼 클릭이벤트의 onClick()함수에서
SoundPool.play()로 사운드파일을 재생해주었습니다.
SoundPool.play()는 짧은 효과음을 재생하기 위한 함수인데

인자값이 6개입니다.
첫번째 인자는 soundID로 좀전에 load()함수의 리턴값으로 저장된 ID를 설정해주면 됩니다.
두번째 인자는 leftVolume로 왼쪽 채널의 볼륨으로 0.0(음소거) ~ 1.0f(최대)사이의 값입니다.
세번째 인자는 rightVolume로 오른쪽 채널의 볼륨으로 0.0(음소거) ~ 1.0f(최대)사이의 값입니다.
네번째 인자는 priority, 우선순위로 0으로 설정하면 다른 사운드가 재생줄일때 대기하며, 숫자가 높을수록 우선순위가 높습니다.
다섯번째 인자는 loop로 반복재생 여부입니다. 0이면 반복하지않고, 1이상은 해당 횟수만큼 반복합니다.
여섯번째 인자는 rate로 재생속도입니다. 1은 정상속도이고 1보다 작은면 느리게, 1보다 크면 빠르게 재생합니다.
soundPool.play(nSoundID, 1f, 1f, 1, 0, 1f);
저는 위와같이 설정했기때문에
nSoundID는 R.raw.oksound이고
좌측볼륨 최대, 우측볼륨 최대, 우선순위 최대, 반복없고 재생속도는 정상속도가 됩니다.
실행을 해보면
"OK 효과음" 눌렀을때 "띠리링" 소리가 잘 들립니다. ^^
그런데, 여기서 한가지 주의할점이 있습니다.
▣ 사운드파일 로드가 완료된 시점에 사운드파일 재생하기
다음 코드를 보겠습니다.
SoundPool soundPool = new SoundPool.Builder().build();
nSoundID = soundPool.load(getApplicationContext(),R.raw.oksound,1);
soundPool.play(nSoundID, 1f, 1f, 1, 0, 1f);
위코드는 버튼클릭이 아니라
SoundPool객체생성하고 load한 후에 바로 play()를 하였습니다.
이렇게하면 앱이 실행되자마자 소리가 나와야 할텐데
실제 앱을 실행해보면 아무소리도 나지않습니다.
이유는 사운드파일에 메모리를 로드하는 시간보다 play()를 해라 라는 명령어 호출시간이 더 빠르기 때문입니다.
즉, 사운드파일을 로드하기위해서는 어느정도 시간이 필요하다는 뜻입니다.
SoundPool soundPool = new SoundPool.Builder().build();
nSoundID = soundPool.load(getApplicationContext(),R.raw.oksound,1);
Button btn_OKSound = findViewById(R.id.btnOKSound);
btn_OKSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
soundPool.play(nSoundID, 1f, 1f, 1, 0, 1f);
}
});
이 코드에서는 앱이 실행되자마자 사운드 파일을 로드하기 시작하고
버튼을 클릭하기까지 어느정도 시간이 필요합니다.
즉, 사운드파일을 로드하는 시간이 버튼클릭하는 시간보다 빠르다는 거죠..
그래서, 이경우에는 사운드 파일을 재생할수가 있습니다.
이런 문제점때문에
사운드파일이 정말 로드가 완료되었는지를 체크한 후에 재생을 해야합니다.
SoundPool soundPool = new SoundPool.Builder().build();
Button btn_OKSound = findViewById(R.id.btnOKSound);
btn_OKSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
nSoundID = soundPool.load(getApplicationContext(),R.raw.oksound,1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int i, int i1) {
soundPool.play(nSoundID, 1f, 1f, 1, 0, 1f);
}
});
}
});
그래서, 위와같이
setOnLoadCoompleteListener()를 이용해서
onLoadComplete()함수 안에서 play()를 해주어야 합니다.
setOnLoadCoompleteListener는 SoundPool을 사용하여 사운드 파일을 로드할때, 로드작업이 완료되었는지 여부를 감지하기 위해 사용하는 콜백리스너 입니다.
로딩이 완료되면 onLoadComplete()함수가 호출됩니다.
onLoadComplete()함수의
첫번째 인자는 현재 SoundPool 객체이고
두번째 인자는 로드된 오디오 스트림의 고유식별자입니다. 여기에서 i값으로 되어있는데 nSoundID랑 같은값인 셈이죠..
세번째 인자는 로드 작업의 성공여부인데 0은 성공을 의미합니다.
자, 이렇게 해서 OK 효과음은 소리를 냈고
NG효과음도 소리를 내보겠습니다.
SoundPool soundPool = new SoundPool.Builder().build();
Button btn_OKSound = findViewById(R.id.btnOKSound);
btn_OKSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
nSoundID = soundPool.load(getApplicationContext(),R.raw.oksound,1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int i, int i1) {
soundPool.play(i, 1f, 1f, 1, 0, 1f);
}
});
}
});
Button btn_NGSound = findViewById(R.id.btnNGSound);
btn_NGSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
nSoundID = soundPool.load(getApplicationContext(),R.raw.ngsound,1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int i, int i1) {
soundPool.play(i, 1f, 1f, 1, 0, 1f);
}
});
}
});
OK, NG 효과음을 내는 전체 코드입니다.
잘되는지 실행을 해보겠습니다.
네.. OK하고 NG효과음이 잘 들리네요..^^
MainActivity.java 전체 소스입니다. 참고하세요~
package com.example.soundtest;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity {
int nSoundID = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
SoundPool soundPool = new SoundPool.Builder().build();
Button btn_OKSound = findViewById(R.id.btnOKSound);
btn_OKSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
nSoundID = soundPool.load(getApplicationContext(),R.raw.oksound,1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int i, int i1) {
soundPool.play(i, 1f, 1f, 1, 0, 1f);
}
});
}
});
Button btn_NGSound = findViewById(R.id.btnNGSound);
btn_NGSound.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
nSoundID = soundPool.load(getApplicationContext(),R.raw.ngsound,1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int i, int i1) {
soundPool.play(i, 1f, 1f, 1, 0, 1f);
}
});
}
});
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
}
이번시간은 여기까지만 하도록 하구요~
다음시간에는 효과음이 아닌 배경음을 재생해보는 것을 해보겠습니다.
그럼, 이만~
감사합니다. ^^
