== 실로폰 ===
- 기능 소개
음판을 누르면 소리가 재생
- 주된 도구
SoundPool (음원을 관리하고 재생하는 클래스. 안드로이드 5.0 이전과 이후 버전의 동작이 다르므로
모든 기기에서 잘 동작하도록 버전 분기를 적용할 것임)
1. 프로젝트 생성
프로젝트명 : Xylophone
miniSdkVersion : 19
기본 액티비티 : Empty
레이아웃 에디터에서 미리보기 모드를 가로모드 작업 환경으로 설정합시다.
(실로폰이 가로 모양이니까요)
2. 텍스트 뷰로 음판 만들기
음판을 적당하게 배치한 후 한 번에 제약을 추가해 봅시다. (Autoconnect 모드 off)
디폴트 텍스트 뷰 삭제 > 팔레트 창 Common > TextView를 끌어다 건반 모양으로 배치
id : do1
layout_width : 50 dp
layout_height : match_constraint
위, 아래 여백 : 16 (좌, 우 여백은 모든 건반 배치후 한 번에 설정할 것임)
text : 도
textAppearance : AppCompat.Large
textColor : @android.color/white
background : holo_red_dark
gravity : center에 체크 (컨텐츠를 가운데 배치하는 역할)
위와 같은 형태로 7개의 테스트 뷰를 추가로 배치.
id |
위, 아래 여백 |
text |
background |
re |
24 |
레 |
@android:color/holo_orange_dark |
mi |
32 |
미 |
@android:color/holo_orange_light |
fa |
40 |
파 |
@android:color/holo_green_light |
sol |
48 |
솔 |
@android:color/holo_blue_light |
la |
56 |
라 |
@android:color/holo_blue_dark |
si |
64 |
시 |
@android:color/holo_purple |
do2 |
72 |
도 |
@android:color/holo_red_dark |
모든 건반 배치가 끝나면 컨트롤 키를 누른 상태에서 모든 건반을 클릭하여 전체 건반 선택 >
마우스 우측 버튼 > 컨텍스트 메뉴에서 Chains > Create Horizontal Chain (뷰들이 체인으로 연결됨)
체인으로 연결된 뷰 중 아무 뷰나 선택 후 컨텍스트 메뉴 > Cycle chain mode 클릭
(클릭할 때마다 세 가지 모드가 전환됨)
(아래와 같이 건판이 균일한 간격이 되는 모드를 선택)
이렇게 텍스트 뷰들로 실로폰 음판을 만들었습니다.
3. 재생할 소리 리소스 준비
wav, mp3 와 같은 사운드 파일은 .raw 리소스 디렉터리를 만들어 그 안에 저장해 놓고 사용합니다.
1) .res 디렉토리 생성
프로젝트 창의 .res 디렉터리에서 마우스 우측 버튼 클릭 > 컨텍스트 메뉴 중 'New > Android Resource Directory'
Resource type : raw
2) http://bit.ly/2K9dQjo 에서 실로폰 음계의 wav 파일 다운로드 > .res에 저장
※ 안드로이드 기기에서 소리 재생
방법1) MediaPlayer 클래스 (음악 및 비디오 파일 재생. 한 번만 재생하고 플레이어를 끝낼 때 유용)
사용 예)
val mediaPlayer = MediaPlayer.create(this, R.raw.do1)
button.setOnClickListener{ mediaPlayer.start() }
...
mediaPlayer.release() // 사용이 끝난 후 해제해 줘야 함
방법2) SoundPool 클래스 (실로폰과 같이 연속으로 소리를 때마다 계속 내야 할 때 유용)
사용 예)
val soundPool = SoundPool.Builder().build()
val soundId = soundPool.load(this, R.raw.do1, 1) // load(컨텍스트, 소리파일, 우선순위)
button.setOnClickListener { soundPool.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f) }
// play(음원id), 왼쪽볼륨 0.0~1.0, 오른쪽 볼륨, 우선순위(0은 최하순위),
// 반복여부(0:반복안함, -1:반복), 재생속도(배속)
4. SoundPool 초기화 버전 분기
-- MainActivity
class MainActivity : AppCompatActivity() {
...
private val soundPool = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
SoundPool.Builder().setMaxStreams(8).build() // 한꺼번에 재생하는 음원 개수 (8개 동시 재생) *1
} else {
SoundPool(8, AudioManager.STREAM_MUSIC, 0) // 최대 재생 스트림 개수, 음원 종류, 음질 (default: 0)
}
}
*1 에서 오류가 표시될 때 Alt+Enter를 눌러 표시되는 제안 중 Sorround with... 를 선택해 주세요.
(버전에 따른 if 분기를 하는 코드가 생성됨)
5. 건반에 동적으로 클릭 이벤트 정의
-- MainActivity (최종 소스 코드)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sounds.forEach { tune(it)} // sounds 리스트를 각각 tune()에 전달
}
private val soundPool = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
SoundPool.Builder().setMaxStreams(8).build() // 한꺼번에 재생하는 음원 개수 (8개 동시 재생) *1
} else {
SoundPool(8, AudioManager.STREAM_MUSIC, 0) // 최대 재생 스트림 개수, 음원 종류, 음질 (default: 0)
}
private val sounds = listOf( // 리스트 객체
Pair(R.id.do1, R.raw.do1),
Pair(R.id.re, R.raw.re),
Pair(R.id.mi, R.raw.mi),
Pair(R.id.fa, R.raw.fa),
Pair(R.id.sol, R.raw.sol),
Pair(R.id.la, R.raw.la),
Pair(R.id.si, R.raw.si),
Pair(R.id.do2, R.raw.do2)
)
private fun tune(pitch: Pair<Int, Int>) {
val soundId = soundPool.load(this, pitch.second, 1) // 음원 id 얻기
findViewById<TextView>(pitch.first).setOnClickListener { // 텍스트 뷰의 id에 해당하는 뷰 얻기
soundPool.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f)
}
}
override fun onDestroy() {
super.onDestroy()
soundPool.release()
}
}
}
실행한 후 각 건반을 터치 해 봅시다.
(건반을 누르면 저장된 음원을 재생하며 소리가 납니다)