본문 바로가기

책/오준석의 안드로이드 생존코딩 코틀린편

12. 실로폰

프로젝트명 Xylophgone
기능
  • 음 판을 누르면 소리가 재생된다.
핵심구성요소
  • SoundPool : 음원을 관리하고 재생하는 클래스이다.
라이브러리 설정 없음
  1. 준비하기 : 프로젝트 생성 및 안드로이드 설정
  2. 스텝1 : 레이아웃 작성
  3. 스텝2 : 소리 재생하기

스텝1 레이아웃 작성

가로 모드로 고정하기

액티비티에서 프로그래밍 방식이로 가로모드 고정 이외에 매니페스트 파일에서 가로 모드 고정이 가능하다. 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="abstractask.example.xylophone">
    <application
    ....
        >
        <!-- android:screenOrientation="landscape"로 설정하여 
        MainActivity액티비티를 가로모드로 고정하였다. -->
        <activity android:name=".MainActivity"
            android:screenOrientation="landscape">
        ....
        </activity>
    </application>
</manifest>

activity_main.xml 레이아웃 파일에서는 가로모드로 디자인 할 수 있도록 다음과 같이 설정한다.

텍스트 뷰로 음판 만들기

다음 배치할 TextView의 공통 속성을 정의 한다.

배치 Autoconnect 비활성화 모드로 TextView를 레이아웃의 좌측에 배치
layout_width 50dp
layout_height match_constraint
textAppearance AppCompat.Large
textColor @android:color/white
background @android:color/holo_red_dark (Resource팝업에서 선택할수도 있음)
gravity center

위의 공통 속성을 기반으로 TextView를 8개를 만들어 배치한다.

ID 위, 아래 여백 text background
do1 16 @android:color/holo_red_dark
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

 

  • 컨트롤 키를 누른 상태로 다른 TextView를 클릭해 모두 선택한다.
  • 마우스 오른쪽 버튼 클릭 -> Chains -> Create Horizontal Chain을 클릭한다. 선택된 모든 뷰는 수평방향 체인으로 설정된다.
  • Chain이 설정된 후에는 Chain Style을 변경할 수 있다.

스텝2 소리 재생하기

raw 리소스 디렉터리 추가

res디렉터리를 선택한 상태에서 File -> New -> Android Resource Directory를 클릭한다. 아래 이미지처럼 선택하고 OK를 클릭한다.

실로폰 소리 파일 준비하기

책의 예제에 있는 링크로 소리파일을 받아와 res/raw 디렉터리에 복사해준다.

링크 : https://github.com/junsuk5/kotlin-android/tree/master/Chapter12/app/src/main/res/raw

 

junsuk5/kotlin-android

오준석의 안드로이드 생존코딩: 코틀린 편. 예제 소스 코드. Contribute to junsuk5/kotlin-android development by creating an account on GitHub.

github.com

안드로이드에서 소리를 재생하는 방법

MediaPlayer

  • 일반적인 소리파일 연주 시 사용
  • 음악파일과 비디오파일 모두 재생가능
  • 소리를 한번만 재생하는 경우 노래나 재경음 같은 경우 유용
// raw 디렉터리의 do1파일을 재생하는 예
val mediaPlayer = MediaPlayer.create(this, R.raw.do1)
button.setOnClickListener{ mediaPlayer.start() }

....

// 사용이 끝나면 릴리즈해야 함
medaiPlayer.release()

SoundPool

  • 연속으로 소리를 재생하는 경우 유용
val soundPool = SoundPool.Builder().build()

/*
  음원을 준비하여 id 반환
  load(context: Context, resId: Int, priorty: Int)
    - context : 컨텍스트를 지정. 액티비티를 지정
    - resId : 재생할 raw 디렉터리의 소리 소스 파일 리소스를 지정
    - priorty : 우선순위를 지정. 숫자가 높으면 우선순위가 높음
*/
val soundId = soundPool.load(this, R.raw.do1, 1)

/*
  음원을 재생
  play(
      soundId: Int,
      leftVolume: Float,
      rightVolume: Float,
      priorty: Int,
      loop: Int,
      rate: Float
  )
    - soundId: load()메서드에서 반환된 음원의 id를 지정
    - leftVolume: 왼쪽 볼륨을 0.0 ~ 1.0 사이에서 지정
    - rightVolume: 오른쪽 볼륨을 0.0 ~ 1.0 사이에서 지정
    - priorty: 우선순위를 지정. 0이 가장 낮은 우선순위
    - loop: 반복을 지정, 0 반복안함, -1 반복
    - rate: 재생속도 지정 1.0 보통, 0.5 0.5배속, 2.0 2.0배속
*/
button.setOnClickListener{ soundPool.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f) }

SoundPool 초기화 및 클릭이벤트 정의

package abstractask.example.xylophone

import android.content.pm.ActivityInfo
import android.media.AudioManager
import android.media.SoundPool
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class MainActivity : AppCompatActivity() {

    private val soundPool = if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
        SoundPool.Builder().setMaxStreams(8).build()
    }else {
    
        // 안드로이 5.0 미만에서 SoundPool 사용방법
        @Suppress("DEPRECATION")
        SoundPool(8, AudioManager.STREAM_MUSIC,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)
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        /*
        // 화면이 가로 모드로 고정되게 하기
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
         */
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        sounds.forEach{ tune(it) }
    }

    private fun tune(pitch: Pair<Int, Int>) {
        val soundId = soundPool.load(this, pitch.second, 1)
        findViewById<TextView>(pitch.first).setOnClickListener{
            soundPool.play(soundId, 1.0f, 1.0f,0,0,1.0f)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        soundPool.release()
    }
}