Tuesday, January 6, 2015

Java กับ Generic Class

เวลาสร้างคลาสในจาวา เราก็คงอยากให้คลาสของเรารองรับตัวแปรหลายๆประเภท แล้วจะทำได้อย่างไร ทางออกหนึ่งก็คือ Generic Class นี่ล่ะครับ เราสามารถกำหนดให้มันใช้ตัวแปรแบบกลางๆ (ไม่ต้องระบุว่าเป็น int, char, double ฯลฯ)

ลองดูตัวอย่างการสร้างตัวแปรของคลาสที่เป็นแบบ int และการ set กับ get ตามปกติกันก่อน

public class MyClass{
    private int value;
    public void set(int v){
        value = v;
    }
    public int get(){
        return value;
    }
    
    public static void main(String[] args){
        MyClass m = new MyClass();
        m.set(5);
        System.out.println(m.get());
    }
}
แน่นอนครับ ผลลัพธ์ที่ได้ก็คือเลข 5 นั่นเอง

คราวนี้ ถ้าเราต้องการส่งค่าที่ไม่ใช่ตัวเลข เช่น m.set("Hello"); ไป สิ่งที่เกิดขึ้นก็คือ compile ไม่ผ่าน เพราะชนิดของข้อมูลที่ส่งไป กับชนิดของข้อมูลที่เอาไปรับไม่ตรงกัน ก็จำเป็นจะต้องเขียน method ใหม่ แถมยังไม่พอ ต้องประกาศตัวแปรของคลาสใหม่เพื่อมารับข้อมูลชนิดนี้ด้วย

เพื่อที่จะลดความซับซ้อนตรงนี้ลง (แต่ไปเพิ่มความงงให้กับการเขียนโปรแกรม) ลองใช้ Generic class กันดูตามนี้ครับ

public class MyClass<T>{
    private T value;
    public void set(T v){
        value = v;
    }
    public T get(){
        return value;
    }
    
    public static void main(String[] args){
        //MyClass m = new MyClass();
        MyClass<Integer> m1 = new MyClass<Integer>();        
        //m.set(10);
        m1.set(new Integer(10));
        System.out.println(m1.get());
        
        MyClass<String> m2 = new MyClass<String>();
        m2.set(new String("Hello"));
        System.out.println(m2.get());
    }
}

ผลลัพธ์ที่ได้คือ
10
Hello

ซึ่งจะสังเกตเห็นว่า เราก็ยังมีคลาสเดียว แต่ตอนสร้าง Object เราสร้างมันสองตัวคือ m1 เป็นแบบ Integer และ m2 เป็นแบบ String แต่ทั้งสอง Object ก็ใช้ตัวแปรคลาสและ method ร่วมกันได้

ข้อสังเกต

  1. เราสร้างคลาสด้วย public class MyClass<T> ซึ่งหลังคลาสจะตามด้วย Generic type ที่ระบุด้วยเครื่องหมาย <T> และตัว T ก็เป็นชื่อชนิด (จะตั้งเป็นตัวอื่นก็ได้ แต่มักใช้ T แทน Type)
  2. จากนั้นเราก็ใช้ T เหมือนชนิดของตัวแปรทั่วไป เช่น private T valueก็คือการกำหนดให้ตัวแปร value เป็นชนิด T ซึ่งจะกลายเป็น reference type เช่น Integer, Double, String ในอนาคตได้
  3. ในส่วนของ main() เวลาสร้าง Object ต้องระบุชนิดตามหลังคลาสด้วย (สังเกตว่าสัญลักษณ์ ตามติดหลังชื่อคลาสตลอด) เช่น MyClass<Integer> m1 = new MyClass<Integer>(); ซึ่งในจาวารุ่น 7.0 ไป เราสามารถลดรูปให้เหลือแค่ MyClass<Integer> m1 = new MyClass<>(); ได้
  4. เวลาส่งค่าให้ method ของ Generic class ในกรณีนี้ เราก็ไม่สามารถส่งค่าตัวแปรแบบทั่วไปได้ ต้องส่งเป็น Object ของคลาสไป เช่น m1.set(new Integer(10)); แทน

Java กับ Generic method

เมื่อเราต้องการเขียนโปรแกรมให้สนับสนุนข้อมูลหลายๆประเภท เช่น เขียน method เดียวแต่รองรับทั้งข้อมูลที่เป็น integer และ String เราอาจจะคิดถึง Overloaded methods ซึ่งก็ต้องเขียน method แยกไปแต่ละประเภทของข้อมูล

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

ลองดูตัวอย่างการเขียนโปรแกรมโดยใช้ method ปกติ เพื่อแสดงค่าของสมาชิกของตัวแปรชุด (Array)

//======= Normal method to print an integer array ==========
public class MyArray{
    public void printArray(int[] arr){
        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }        
    }
    
    public static void main(String[] args){
        MyArray m = new MyArray();
        int[] a = {1,2,3};
        m.printArray(a);
    }
}
จะเห็นว่า method ชื่อ printArray() ก็จะใช้ได้เฉพาะ array แบบ integer เท่านั้น ถ้าต้องการใช้กับข้อมูลประเภทอื่น ก็ต้องเขียน method นี้ใหม่

คราวนี้ลองมาเปลี่ยน method นี้ให้เป็น generic method กันดูครับ

//======= Generic method to print any array ==========
public class MyArray{
    public <E> void printArray(E[] arr){
        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }        
    }
    
    public static void main(String[] args){
        MyArray m = new MyArray();
        //int[] a = {1,2,3};
        //must create array object to be compatible with generic type
        Integer[] a = {1,2,3};
        String[] b = {"I","love","you"};    
        m.printArray(a);
        m.printArray(b);
    }
}
ก็จะเห็นว่า method ใหม่นี้รองรับทั้ง array หลายชนิด ทั้งแบบ Integer และ String ตามตัวอย่าง

ข้อสังเกตการสร้างและใช้งาน generic method คือ

  1. Generic method มี <Eหน้า return type ซึ่งสัญลักษณ์พิเศษนี้ก็คือตัวแทนของชนิดของข้อมูล นั่นคือ E ในที่นี้อาจจะแทน Integer, String, Double ฯลฯ (เป็น reference type ไม่ใช่ primitive type เช่น int, double, char)
  2. เราสามารถใช้ตัวอักษรอื่นแทน E ก็ได้ ซึ่งปกติจะมีรูปแบบแนะนำว่าควรใช้ตัวอักษรแบบไหนเพื่ออะไร เช่น E แทน Elements หรือ T แทน Types ดูเพิ่มเติมได้ที่นี่ครับ
  3. การใช้งานตัว E ใน method ก็เหมือนชนิดของข้อมูลทั่วไป เช่น เอาไว้หน้าตัวแปร
ข้อมูลอ้างอิง
http://www.tutorialspoint.com/java/java_generics.htm
http://docs.oracle.com/javase/tutorial/java/generics/

Friday, January 2, 2015

Processing กับเสียง ตอนที่ 3 เสียง Background กับ Event

ได้เวลายำสองตอนก่อนหน้านี้เข้าด้วยกันแล้วครับ เราจะลองสร้างโปรแกรมง่ายๆ โดยให้เมื่อรันโปรแกรมก็จะเล่นเสียงเพลงเป็น Background music และมีวงกลมสีแดงเกิดขึ้น เมื่อเคลื่อนเมาส์ไปแถวๆวงกลม สีจะเปลี่ยนเป็นสีเหลือง และเล่น Event sound ขึ้นมาหนึ่งครั้ง



สมมติว่ามีไฟล์เสียงสองไฟล์ เก็บไว้ใน data folder ของโปรเจคแล้วตามนี้
1. Background music คือ POL-icy-town-short.wav
2. Event sound คือ coin.wav

โค้ดก็จะประมาณนี้ครับ
import ddf.minim.*;

Minim minim;
AudioSample coin;
AudioPlayer player;
int flag=0;
color cl = #FF0000;

void setup()
{
  size(200, 200);
  minim = new Minim(this);
  player = minim.loadFile("POL-icy-town-short.wav");
  player.loop();
  coin = minim.loadSample("coin.wav", 512);
}

void draw()
{
  background(0);  
  fill(cl);
  ellipse(100,100,50,50);
  if(flag==0){
    if(mouseX>=80 && mouseX<=120){
      if(mouseY>=80 && mouseY<=120){
        coin.trigger();
        flag=1;
        cl=#FFFF00;
      }
    }
  }
}

สังเกตว่า การเล่นเสียงทั้งสองเสียงถูกแยกเป็น thread ต่างหากกันครับ สามารถเล่นได้พร้อมๆกันได้เลย

Processing กับเสียง ตอนที่ 2 Event Sound/ Sound Effects

คราวนี้เราจะมาลองกับเสียงสั้นๆ ที่ดังก็ต่อเมื่อเราทำอะไรสักอย่างกับโปรแกรม เช่น เลื่อนเมาส์ไปตำแหน่งที่กำหนด หรือคลิก ฯลฯ

ถ้าเป็นเสียงสั้นๆ ใน reference ของ Minim บอกว่าควรจะใช้คลาสชื่อ AudioSample แทน AudioPlayer เพราะว่าเสียงสั้นควรจะถูกโหลดไปไว้ใน memory เลย

สมมติว่าเราสร้างโปรแกรมง่ายๆ ให้มีวงกลมตรงกลางจอ จากนั้นถ้าเลื่อนเมาส์ไปวางแถวๆวงกลม ให้เกิดเสียง ถ้าเอาเมาส์ออก เสียงก็จะหายไป



อันดับแรก ไปหาเสียงพวก Event Sound นี้ก่อน ถ้าลองค้นหาด้วยคำว่า free sound effects ก็จะเจอเยอะแยะมากมาย มีเว็บนึงที่สามารถสร้างเสียงเหล่านี้ขึ้นได้เองเลย คือ http://www.bfxr.net/ ลองไปเล่นดูได้ครับ

สมมุติว่าเรามีไฟล์เสียงอันนึง ชื่อ coin.wav แล้วย้ายไปไว้ยัง data folder ของโปรเจคแล้ว

โค้ดก็จะประมาณนี้ครับ
import ddf.minim.*;

Minim minim;
AudioSample coin;

void setup()
{
  size(200, 200);
  minim = new Minim(this);
  //load sound to memory with buffer 512 KB
  coin = minim.loadSample("coin.wav", 512);
}

void draw()
{
  background(0);  
  fill(255,0,0);
  ellipse(100,100,50,50);
  if(mouseX>=80 && mouseX<=120){
      if(mouseY>=80 && mouseY<=120){
        //trigger a sound
        coin.trigger();
      }
  }
}

Thursday, January 1, 2015

Processing กับเสียง

มีโครงงานอันนึงทำด้วย Processing ต้องการเพิ่มเสียงเข้าไป ทั้ง Background music และ Event sound ก็เลยต้องหาวิธีการเล่นไฟล์เสียง

ก่อนอื่นก็ต้องไปหาไฟล์เสียงก่อน เน้นของฟรี (Royalty-free) ซึ่งหาดูแล้วมีมากมายครับ เช่น http://www.playonloop.com/music-loops-category/videogame/

แต่ก่อนจะดาวน์โหลด ให้อ่านดูดีๆนะครับว่าเค้ามี license แบบไหน เช่น Creative Commons ก็สามารถนำไปใช้ได้ฟรีทั้งแจกจ่ายและแก้ไข รวมถึงเพื่อการค้า แต่ต้อง You must give appropriate credit, provide a link to the license, and indicate if changes were made.

เมื่อได้ไฟล์เสียงที่ต้องการแล้ว อาจจะเป็นไฟล์ wav หรือ mp3 ก็ได้ ก็ให้ย้ายไปยัง Sketch folder (เปิด Processing สร้างโปรเจคแล้วเซฟไว้ แล้วเลือกเมนู Sketch/Show Sketch folder หรือ Ctrl+K จากนั้นคัดลอกไฟล์ไปไว้ยังโฟลเดอร์ data ในโปรเจคที่เปิดอยู่) ในที่นี้สมมติว่าไฟล์เสียงของเราชื่อ POL-icy-town-short.wav ครับ

ขั้นตอนต่อไปคือเลือกใช้ sound library สำหรับ Processing แล้วจะมีหลายตัว ตัวที่เป็นตัวพื้นฐานของ Processing เองเลย คือ https://processing.org/reference/libraries/sound/index.html ซึ่งเท่าที่ลองอ่านดู จำเป็นต้องติดตั้งต่างหากเพิ่มเติม ซึ่งก็ไม่ยากอะไร เพียงเลือกเมนู Sketch/Import Library/Add Library แล้วเลือก Sound รอดาวน์โหลดแป้บนึงก็เรียบร้อย อย่างไรก็ตาม ณ ปัจจุบัน library ตัวนี้ยังสนับสนุนเฉพาะ OS แบบ 64 บิตเท่านั้น เครื่องผมเป็น 32 บิตเลยแห้วไป

เมื่อใช้ตัวปกติของ Processing ไม่ได้ ก็ต้องไปหา library ตัวใหม่ ผมพบว่าถ้าเป็น Processing 2 จะมี library ตัวนึงแถมมาให้เลย ชื่อ minim ไม่เข้าใจเหมือนกันว่าทำไม Processing 3 (ซึ่งปัจจุบันเป็นตัวเบต้าอยู่) ถอดออกไป และเลือกที่จะพัฒนาเอง

แต่ช่างมันเถอะครับ เลือกตัวที่เราใช้ได้ตอนนี้ดีกว่า ถ้าใครใช้ Processing 2 อยู่แล้วก็ไม่มีปัญหา ส่วนใครใช้ Processing 3 beta ก็ไปดาวน์โหลด minim ที่ http://code.compartmental.net/tools/minim/ จะได้ไฟล์ zip ออกมา แตกไฟล์ไปไว้ที่ sketchbook location เช่น C:\Users\user\Documents\Processing\libraries ได้เลย ข้างในโฟลเดอร์จะมีไฟล์มากมาย ซึ่งเราสามารถลบให้เหลือเฉพาะโฟลเดอร์ชื่อ library ได้

จากนั้นลองเริ่มเขียนโค้ดกันดู สมมติว่ามีไฟล์เสียงในโฟลเดอร์ data ของโปรเจคปัจจุบันแล้วนะครับ
import ddf.minim.*;

Minim minim;
AudioPlayer player;

void setup()
{
  size(200, 200);  
  minim = new Minim(this);
  //load music file
  player = minim.loadFile("POL-icy-town-short.wav");
  //play music
  player.play();
}

void draw(){
  background(0);
}

ลองรันดูครับ น่าจะได้ยินเสียงตามต้องการแล้ว

นอกจากนี้แล้ว ยังมีฟังก์ชันที่น่าสนใจอีกมากมาย เช่น
-ถ้าต้องการให้เล่นไฟล์แบบวนไปเรื่อยๆ ก็เปลี่ยนจาก player.play() เป็น player.loop() หรือถ้าต้องการให้วนกี่ครั้งก็ใช้ player.loop(num) เมื่อ num คือจำนวนครั้งที่ต้องการให้เล่นซ้ำครับ
-ถ้าต้องการหยุดชั่วคราว ใช้ฟังก์ชัน pause
-ถ้าต้องการปิด/เปิดเสียง ใช้ฟังก์ชัน mute/unmute

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