22 July 2013

การเพิ่มเสียงประกอบลงใน Application [BGM & Effect]

Updated on

รอบนี้ขอข้ามมาเป็นเรื่องเสียงกันบ้างดีกว่า เปลี่ยนอารมณ์กันบ้าง
ซึ่งเจ้าของบล็อกจะขอพูดถึงการเพิ่มเสียงเข้าไปในแอปพลิเคชัน
เพราะเสียงประกอบก็เป็นอีกหนึ่งหัวใจสำคัญของแอปพลิเคชันเช่นกัน
โดยเฉพาะจำพวกเกมต่างๆ ที่จะขาดเสียงประกอบไปไม่ได้เลย


ก่อนอื่นเลยขอพูดถึงเรื่องเสียงที่นำมาใช้กันก่อนละกันเนอะ

สำหรับเสียงที่จะใช้สำหรับเวลากดปุ่ม หรือกดเพื่อกระทำการใด
จะเรียกกันว่า Effect หรือก็คือเสียงประกอบการกระทำใดๆก็ตาม

ส่วนเสียงเพลงที่เอาไว้คลอเบาๆเวลาที่ผู้ใช้เปิดแอปพลิเคชันอยู่
อย่างเช่น เสียงเพลงประกอบฉากใดๆในเกม เรียกว่า BGM

สำหรับเสียงที่จะนำมาใช้ในแอปพลิเคชันของผู้ที่หลงเข้ามาอ่าน
ให้คำนึงถึงความเหมาะสมกับแอปพลิเคชันด้วยว่าควรเป็นเสียงอะไร
อย่าให้เสียงดังเกินไปหรือว่าเบาเกินไป หรือรู้สึกขัดหูจนเกินไป
และพยายามหลีกเลี่ยงไฟล์ที่มีลิขสิทธิ์ เพื่อจะได้ไม่เกิดปัญหาทีหลัง
โดยเฉพาะกับแอปพลิเคชันที่จะกำหนดให้เสียเงินซื้อ

* เวลาเจ้าของบล็อกจะปรับแต่งเสียง ก็ทำใน Adobe Audition *
* เนื่องจากเจ้าของบล็อกใช้อยู่ประจำตั้งแต่สมัยก่อนแล้ว *


และสำหรับ Android จะรองรับไฟล์เสียง .aac .mp3 .flac .wav และ .mid 
ซึ่งจริงๆ มีมากกว่านี้ แต่มักจะใช้กันอยู่แค่สองแบบคือ .wav กับ .mp3

ทีนี้มาว่าในฝั่งโค๊ดกันบ้าง สำหรับเล่นไฟล์เสียงจะใช้คลาส MediaPlayer
ซึ่งเป็นคลาสที่เอาไว้เล่นไฟล์ Media จำพวกไฟล์วีดีโอหรือไฟล์เสียง
สำหรับไฟล์ Media สามารถเก็บไว้ใน assets ก็ได้หรือเก็บไว้ใน res/raw ก็ได้
หรือแม้กระทั่งเรียกไฟล์จาก External Storage หรือ SD card ก็ได้เช่นกัน
แต่ในบทความนี้จะเก็บไฟล์เสียงไว้ใน res/raw ซึ่งเป็นโฟลเดอร์สำหรับ
เก็บไฟล์ดิบหรือก็คือไฟล์ที่นอกเหนือจาก drawable ที่จะเก็บไว้ใน Resource 


ในตัวอย่างจะเห็นว่าเจ้าของบล็อกเอาเสียงไว้ใน res/raw
โดย bgm.mp3 เป็น BGM และ sound_effect.wav เป็น Effect

ส่วนคำสั่งสำหรับ MediaPlayer ก็จะเรียกใช้งานดังนี้
MediaPlayer mp = MediaPlayer.create(ActivityName.this, R.raw.xxxx);
ActivityName ให้กำหนดให้ตรงกับชื่อ Activity นั้นๆที่เรียกใช้คำสั่งนี้อยู่

เวลาต้องการให้เล่นไฟล์เสียงตามที่กำหนดก็ให้ใช้คำสั่งดังนี้
mp.start();
เพียงเท่านี้ก็เป็นการเล่นไฟล์เสียงนั้นๆแล้ว

และถ้าต้องการให้หยุดเล่นชั่วคราวก็ใช้คำสั่ง
mp.pause();
เมื่อต้องการให้กลับมาเล่นต่ออีกครั้งก็ใช้ start เหมือนเดิม

และถ้าต้องการหยุดเล่นเพลงเลยก็ให้ใช้คำสั่ง
mp.stop();
หลังจาก Stop แล้ว เมื่อใช้คำสั่ง Start ก็จะเป็นการเล่นเสียงใหม่อีกครั้ง

ถ้าใช้งานเสร็จแล้วต้องการเคลียร์คลาสนี้ทิ้งก็ใช้คำสั่งดังนี้
mp.stop(); mp.release(); mp = null;

สำหรับบทความนี้ ก็จะกำหนดให้มี Button ที่กดแล้วมีเสียง Effect
และ Toggle Button อีกปุ่มเอาไว้เลือกว่าจะเปิดหรือปิด BGM

main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#353535" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".Main" > <Button android:id="@+id/btnOK" android:layout_width="150dp" android:layout_height="150dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="@drawable/button_ok" android:text="OK" android:textColor="#2db4e5" android:textSize="30sp" android:textStyle="bold" /> <ToggleButton android:id="@+id/tbBGM" android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:background="@drawable/button_music" android:textOn="" android:textOff="" android:checked="true" /> </RelativeLayout>


ปุ่ม OK จะเป็น Button และปุ่มโน๊ตดนตรีเป็น Toggle Button
โดยมีการตกแต่งปุ่มเล็กน้อย (ทำเป็น Custom Button)

Main.java
package app.akexorcist.soundeffect; import android.media.MediaPlayer; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ToggleButton; public class Main extends Activity { MediaPlayer mpBgm; Button btnOK; ToggleButton tbBGM; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mpBgm = MediaPlayer.create(Main.this, R.raw.bgm); mpBgm.setLooping(true); mpBgm.start(); btnOK = (Button)findViewById(R.id.btnOK); btnOK.setOnClickListener(new OnClickListener() { public void onClick(View v) { MediaPlayer mpEffect = MediaPlayer.create(Main.this, R.raw.sound_effect); mpEffect.start(); } }); tbBGM = (ToggleButton)findViewById(R.id.tbBGM); tbBGM.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton arg0, boolean arg1) { if(arg1) mpBgm.start(); else mpBgm.pause(); } }); } public void onResume() { super.onResume(); if(tbBGM.isChecked()) mpBgm.start(); } public void onPause() { super.onPause(); mpBgm.pause(); } public void onDestroy() { super.onDestroy(); mpBgm.stop(); mpBgm.release(); mpBgm = null; } }


1. ประกาศ Object ทั้ง 3 Class ประกอบด้วย MediaPlayer สำหรับเล่นไฟล์เสียง
Button และ ToggleButton สำหรับควบคุม Widget ที่อยู่บนหน้าแอปพลิเคชัน

2. กำหนดค่าให้กับ mpBgm ของคลาส MediaPlayer โดยกำหนดไฟล์
ให้เป็นไฟล์ที่อยู่ใน res/raw ที่ชื่อว่า bgm แล้วกำหนดให้วนลูปตลอด
แล้วก็สั่งให้เริ่มเล่นไฟล์เสียงดังกล่าวทันที ผลก็คือเมื่อผู้ใช้เปิดแอปฯ
ก็จะมีเสียงเพลง BGM เล่นขึ้นทันทีที่เปิดแอปพลิเคชันขึ้นมา

3. กำหนดค่าให้กับ btnOKของคลาส Button และเรียกใช้ Listener
เมื่อผู้ใช้กดปุ่ม btnOK ก็จะเรียกใช้คลาส MediaPlayer
ให้เล่นไฟล์เสียงที่อยู่ใน res/raw ที่ชื่อว่า sound_effect
ผลก็คือเมื่อกดปุ่มดังกล่าว ก็จะมีเสียง Effect ดังขึ้น

4. กำหนดค่าให้กับ tbBGM ของคลาส ToggleButton และเรียกใช้ Listener
เมื่อกดให้ปุ่ม Toggle Button เป็น ON (True) ก็จะให้เล่นเสียง BGM
และถ้ากดให้เป็น OFF (False) ก็จะให้หยุดเล่นเสียง BGM ชั่วคราว
ซึ่งปุ่มนี้ก็มีไว้เพื่อให้ผู้ใช้เปิดปิดเสียง BGM นั่นเอง

5. ฟังก์ชัน onResume ที่จะทำงานเมื่อเปิดแอปพลิเคชันขึ้นมา
หรือว่าย่อแอปพลิเคชันไว้ แล้วเปิดเข้ามาใหม่อีกครั้ง
โดยฟังก์ชันนี้ก็จะให้เช็คว่าปุ่ม tbBGM ที่เป็น Toggle Button
ว่าปุ่มดังกล่าวเป็น ON หรือ OFF ถ้ามีสถานะเป็น ON
ก็จะให้เล่นไฟล์เสียง BGM ต่อจากของเดิม

ฟังก์ชันนี้มีไว้เพื่อเวลาที่ผู้ใช้ย่อแอพฯ เพลง BGM ก็จะหยุด
แต่เมื่อกลับเข้ามาใหม่ก็จะเล่นเสียง BGM ต่อจากเดิมทันที
ซึ่งผู้ที่หลงเข้ามาอ่านหลายๆคนมักจะพลาดจุดนี้ไป
เมื่อกลับเข้าแอปพลิเคชันอีกครั้ง เสียง BGM ก็จะไม่เล่นต่อ

และจะเห็นว่ามีการเช็คสถานะของปุ่ม tbBgm ด้วย
ทั้งนี้ก็เพราะว่าแอปพลิเคชันนี้เปิด-ปิดเสียง BGM ได้
ดังนั้นถ้าผู้ใช้เปิดเสียง BGM อยู่ (tbBgm เป็น ON)
แล้วทำการย่อแอปพลิเคชันและเปิดเข้ามาต่อ
เสียงก็ควรจะเล่นต่อจากของเดิมอย่างที่กล่าวไป
แต่ถ้าผู้ใช้ปิดเสียง BGM  (tbBgm เป็น OFF)
เมื่อแอปพลิเคชันถูกย่อลงและเปิดเข้ามาต่อ
เสียงก็ไม่ควรเล่น เพราะว่าผู้ใช้ได้ปิดเสี้ยง BGM ไว้

6. ฟังก์ชัน onPause ที่จะทำงานเมื่อแอปฯหยุดทำงานชั่วคราว
ก็จะให้หยุดเสียง BGM ก่อน เพราะไม่เช่นนั้นจะเกิดปัญหา
เวลาที่ผู้ใช้ย่อแอปพลิเคชัน แต่เสียงยังคงเล่นอยู่นั่นเอง
จึงให้หยุดเล่นชั่วคราวก่อน แล้วสั่งงานให้เล่นต่อใน onResume

7. ฟังก์ชัน onDestroy ที่จะทำงานเมื่อแอปฯถูกปิดลง
ก็จะให้หยุดเล่นเสียง BGM แล้วเคลียร์ข้อมูลใน mpBgm ทิ้ง
ซึ่งต่างจากกรณีของ onPause ที่หยุดเล่นชั่วคราว
แต่ในกรณีนี้เป็นการยกเลิกการเล่นไฟล์เสียงไปเลย

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.akexorcist.soundeffect" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="app.akexorcist.soundeffect.Main" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>


พอทำการทดสอบ เมื่อผู้ใช้กดปุ่ม OK ก็จะมีเสียง Effect 
และจะมีเสียง BGM ที่เล่นทันทีที่เปิดแอปพลิเคชันนี้ขึ้นมา
โดยสามารถเปิด-ปิดได้ด้วยการกดที่ปุ่ม Toggle Button
ที่สำคัญและอยากให้เน้นคือ เวลาที่ผู้ใช้ปิดแอปพลิเคชัน
หรือย่อแอปพลิเคชันเสียงจะต้องหยุดและเล่นอย่างถูกต้อง
ไม่ใช่ว่าปิดแอปพลิเคชัน แต่เสียงยังคงเล่นอยู่จนจบซะงั้น

บทความนี้เจ้าของบล็อกไม่ได้เน้นไปที่วิธีการเล่นเสียงหรอก
เพราะว่าเป็นโค๊ดที่ง่าย แต่อยากให้เน้นในการจัดการกับการเล่นเสียง
ว่าควรหยุดเล่นเมื่อใด ควรเล่นต่อตอนไหน และควรยกเลิกเมื่อไหร
ซึ่งมีผู้ที่หลงเข้ามาอ่านที่ไม่สามารถจัดการกับขั้นตอนดังกล่าวได้

สำหรับผู้ที่หลงเข้ามาอ่านคนใดที่ต้องการไฟล์ตัวอย่าง
สามารถดาวน์โหลดได้จาก Sound Effect [Google Drive]


เมื่อเล่นเสียงได้แล้ว ใช่ว่าจะสำเร็จเสร็จสิ้นนะ 
เพราะมันยังไม่สมบูรณ์หรอกกับโค๊ดแค่นี้
จริงๆจะมีวิธีจัดการกับไฟล์เสียงให้ครบครันอยู่ 
สามารถติดตามไปอ่านต่อกันได้ที่