วันพฤหัสบดีที่ 18 ตุลาคม พ.ศ. 2555

สังเคราะห์เสียงฆ้องบน Android ด้วย libpd ตอนที่ 1

หลังจากหัดเขียนโปรแกรมบน Android และทดลองเขียนโปรแกรมฆ้องวงขึ้นมา ก็รู้สึกถึงข้อจำกัดของการใช้เสียงฆ้องที่บันทึกไว้ล่วงหน้าซึ่งก็คือ
  1. มีข้อจำกัดด้านคุณภาพของการบันทึกเสียงและ
  2. ไม่สามารถปรับค่าความถี่และอัตราลดทอนของเสียงได้
แต่การสังเคราะห์เสียงโดยใช้ภาษา Java โดยตรงดูจะไม่ใช่ทางเลือกที่ดีนัก ยิ่งไม่ใช่ทางเลือกที่ดีสำหรับคนที่ไม่เชี่ยวชาญ Java อย่างผม หลังจากที่เสาะหากรรมวิธีเหมาะ ๆ ได้สักพักก็เจอเครื่องมือด้านการสังเคราะห์เสียงจำนวนหนึ่งคือ
ผมได้ทดลองใช้ Chuck กับ Python เพื่อสังเคราะห์เสียงบน Linux ดูแล้วก็ใช้งานได้สะดวกดี แต่ Chuck ไม่มีเครื่องมือที่จะเชื่อมต่อกับ Java และ Android (หรือมีแต่ผมหาไม่เจอ) ส่วน Csound นั้นผมไม่เคยใช้ ค้นดูก็ไม่เจอเครื่องมือที่จะเชื่อมต่อกับ Java และ Android เช่นกัน

สำหรับ puredata นั้นเดิมผมเคยยอมแพ้มันมาแล้วหนหนึ่งด้วยเหตุว่าเขียนแพตช์ (Patch) ของ puredata ไปแล้วก็เอาไปใช้ในโปรแกรมอื่นเหมือน Chuck ไม่ได้...จนกระทั่งผมได้พบกับ libpd จึงได้ทราบว่าผมคิดผิด

อันที่จริง puredata สามารถสื่อสารกับภาษาโปรแกรมอื่น ๆ ได้ผ่านทาง libpd ขั้นตอนการพัฒนาโปรแกรมโดยใช้ libpd จะเป็นดังนี้
  1. ผู้พัฒนาจะสร้างแพตช์ puredata ขึ้นมาก่อน โดยกำหนดชื่อตัวแปรควบคุมไว้แทนอินเลต
  2. เปิดแพตช์ที่สร้างขึ้นจากคำสั่ง openPatch
  3. ส่ง Bang Float Message ผ่านคำสั่ง sendBang sendFloat และ sendMessage ไปยังแพตช์เป้าหมายตามลำดับ
เนื่องจากมีขั้นตอนที่เกี่ยวข้องแยกเป็น 2 ส่วนใหญ่ ๆ ที่ไม่เกี่ยวข้องกันก็คือส่วนการสังเคราะห์เสียงด้วย pd เป็นส่วนหนึ่ง และการตั้งค่าและเรียกใช้ libpd ในแอนดรอยด์เป็นอีกส่วนหนึ่ง ในตอนนี้จึงขอบันทึกไว้เฉพาะส่วนการสังเคราะห์เสียงก่อน

การสังเคราะห์เสียงฆ้องด้วย pd
จากการวิเคราะห์เสียงฆ้องในเบื้องต้นพบว่าคุณลักษณะพื้นฐานที่โดดเด่นของเสียงฆ้องคือองค์ประกอบ Exponentially Decayed Sinusoid ซึ่งสามารถสร้างได้โดยแพตช์ดังแสดงในรูป
andgong.pd
อินเลตของแพตช์นี้จะเป็นค่าความถี่ของลูกฆ้อง เมื่อมี float เข้ามาทางอินเลต bang จะถูกกระตุ้นและจะสร้างอาเรย์ของตัวแปรเวลา t ขึ้นมา ตัวแปรเวลานี้จะคูณเข้ากับอัตราการลดทอนของเสียงฆ้องซึ่งในที่นี้เป็น -2 เมื่อข้อมูลนี้ผ่านออบเจ็กต์ [exp~] ก็จะเป็นฟังก์ชัน
amath f(t)=e^{-2t} endamath
ส่วนตัวเลขความถี่ที่ป้อนเข้ามาแต่แรกก็จะไปกระตุ้น [osc~] เพื่อให้สร้างสัญญาณไซนูซอยด์ออกมา เมื่อนำมาคูณกับเอกโพเนนเชียลในตอนแรกและคูณกับ 0.3 ก่อนส่งออกทางเอาต์เลตจะทำให้ได้ฟังก์ชัน
amath g(t) = 0.3 e^{-2t} cos(2\pi f t) endamath
จะเป็นเสียงฆ้องลูกทวนหรือว่าลูกยอดก็แล้วแต่ว่าความถี่ที่ป้อนเข้าทางอินเลตนั้นเป็นเท่าใด

แต่ [timearray] ไม่ใช่ออบเจกต์มาตรฐานของ pd แต่เป็นออบเจกต์ที่ผมสร้างขึ้นมาเองเพื่อสร้างตัวแปร t ที่วิ่งจาก 0 ถึง 5000 ในเวลา 5 วินาที (ขั้นละ 1 มิลลิวินาที) ทุกครั้งที่มีการกระตุ้นออบเจกต์ด้วย [bang( ซึ่งกลไกของ [timearray] แสดงดังรูปต่อไปนี้
timearray.pd
เมื่อมี [bang( เข้ามาทางอินเลต ข้อความ 0 จะถูกกระตุ้นเพื่อส่งให้ [line~] กระโดดจากค่าใด ๆ ก็ตามขณะนั้นไปที่ [0( ทันที หลังจากนั้น 1 มิลลิวินาที [bang( ที่ถูกหน่วงเวลาไว้ก็จะกระตุ้นข้อความ [5 5000( เพื่อส่งให้ [line~] ทำให้เอาต์พุตของ [line~] เปลี่ยนแปลงจาก 0 ไปเป็น 5 ภายในเวลา 5000 มิลลิวินาที นั่นหมายความว่าเอาต์เลตของแพตช์นี้ก็คือค่าเวลาเป็นวินาทีขั้นละ 1 มิลลิวินาทีเป็นเวลา 5 วินาทีนั่นเอง

เนื่องจาก libpd อนุญาตให้มีแพตช์ Instance เดียว เราก็เลยต้องสร้างลูกฆ้องทั้ง 16 ลูกในแพตช์โดยให้ฆ้องแต่ละลูกถูกกระตุ้นโดยตัวแปร h01 - h16 ด้วยตัวเลขค่าความถี่
gongwong.pd
แม้ว่าจะตีฆ้องทีละลูกหรืออย่างมาก 2 ลูก เพื่อให้การเชื่อมต่อสัญญาณกับ [dac~] ไม่ติดขัดเพราะเชื่อมกับอินพุตมากเกินไปเราเลยใช้ [+~] รวมสัญญาณจากฆ้องแต่ละลูกเข้าด้วยกัน แต่ Hot Inlet คืออินเลตทางซ้ายเท่านั้น เราเลยต้องให้ตัวแปรความถี่ลูกฆ้องลูกที่ 2 - 16 กระตุ้น [bang( เพื่อกระตุ้น Hot Inlet ของ [+~] ตัวแรกด้วย นอกจากนี้ก่อนจะส่งสัญญาณเสียงออกมาทาง [dac~] เราจะป้องกันองค์ประกอบไฟตรง (DC Component) โดยใช้ตัวกรองความถี่สูงผ่าน [hip~] โดยตั้งค่าความถี่ตัดไว้ที่ 1 เฮิร์ทซ และป้องกันการทำงานผิดพลาดของ libpd โดยการขริบสัญญาณขาออกไว้ให้ไม่เกิน +/- 1

เราสามารถทดสอบแพตช์ gongwong.pd ใน puredata ได้โดยการเปลี่ยน [r h01] ถึง [r h16] ให้เป็นข้อความ [xxx01( - [xxx16( รวม 16 ข้อความเมื่อ xxx01 - xxx16 คือค่าความถี่เสียงที่เราต้องการ และทดลองฟังเสียงโดยการคลิ๊กที่ข้อความแต่ละข้อความนั้น ๆ

เมื่อเราเตรียมแพตช์ของเราเรียบร้อยแล้ว ในตอนต่อไปเราจะมาดูว่าจะสามารถเรียกใช้แพตช์นี้ผ่าน libpd เพื่อเขียนโปรแกรมสำหรับแอนดรอยด์ได้อย่างไร

วันพฤหัสบดีที่ 13 กันยายน พ.ศ. 2555

การติดตั้ง Arduino Sketch ลงในไมโครคอนโทรลเลอร์


ผมสนุกสนานกับการพัฒนาโครงงานเล็ก ๆ น้อย ๆ โดยใช้ Arduino มาระยะหนึ่ง สิ่งที่รบกวนจิตใจมาตลอดก็คือเมื่อจะพัฒนาโครงงานใหม่ ผมจะต้องรื้อโครงงานเก่าเพื่อนำบอร์ด Arduino ออกมาใช้ใหม่เสมอ

จนกระทั่งมีนักศึกษาทักว่าถ้าต้องติดตั้งบอร์ด Arduino ลงไปในโครงงานทุกคราวไปมันจะไม่สิ้นเปลืองไปหน่อยหรือ? คิดในมุมกลับก็คือถ้ามันไม่มีวิธีที่ประหยัดกว่านี้ การพัฒนาชิ้นงานด้วย Arduino ก็ไม่น่าจะได้รับความนิยมกว้างขวางขนาดนี้หรอก เออแฮะ! นั่นก็จริง

เมื่อคิดได้ดังนั้นแล้วก็เลยทำการค้นคว้าข้อมูลอินเตอร์เนตดู จึงพบว่าโครงการ Arduino มีความสามารถที่จะโปรแกรม Sketch ลงไปในไอซีไมโครคอนโทรลเลอร์เพื่อนำไปใช้แบบเดี่ยว ๆ (Standalone) ได้ด้วย ทำให้หลังจากที่เราพัฒนา Sketch บนบอร์ดทดลอง Arduino และทดลองวงจรแล้วเราสามารถติดตั้ง Sketch นั้นกับไอซีไมโครคอนโทรลเลอร์เปล่า ๆ ก็ได้ (เข้าใจผิดมาตั้งนาน) จากนั้นจึงติดตั้งไมโครคอนโทรลเลอร์นั้นลงในวงจรได้เลยโดยที่ไม่ต้องเสียสละบอร์ด Arduino ของเราลงไปในทุกโครงการ ในการนี้เราจำเป็นจะต้องมีอุปกรณ์หลัก 2 สิ่งคือ
  1. บอร์ด Arduino รุ่นใดก็ได้เพื่อใช้เป็นตัวโปรแกรม (Programmer) และ
  2. ไอซีไมโครคอนโทรเลอร์ตระกูล AVR ที่จะใช้แทนบอร์ด Arduino
ไอซีไมโครคอนโทรลเลอร์ที่จะใช้จะต้องสอดคล้องกับบอร์ดที่เราจะเอาไอซีไปใช้แทนด้วย รายการชื่อบอร์ดและไอซีไมโครคอนโทรลเลอร์ที่เข้าคู่กันน่าจะหาได้จากเว็บไซต์ของ Arduino เอง แต่ผมมีข้อเสนอแนะว่าใช้ไอซีพื้น ๆ กับบอร์ดพื้น ๆ นี่แหละ ดีที่สุดเพราะมันจะเป็นไอซีและบอร์ดที่มีคนลองมาแล้วว่าใช้ได้แน่นอนไม่มีปัญหา รายการที่ผมพอทราบว่าใช้การได้ก็มีดังนี้ครับ
  • Diecimila (ET-EASY168, POP-168) ใช้ Atmega168
  • Arduino Mega (ET-EASYMEGA1280) ใช้ Atmega1280
  • Arduino NG w/ Atmega168 ใช้ Atmega168
  • Arduino NG w/ Atmega8 ใช้ Atmega8 <=== ผมใช้ตัวนี้เพราะหาไอซี Atmega8 ได้
กระบวนการนี้มีขั้นตอนสำคัญ 2 ส่วนคือ
  1. ทำให้บอร์ด Arduino ของเรากลายเป็นตัวโปรแกรม
  2. ติดตั้ง Arduino Sketch ของโครงงานของเราลงในไอซี AVR ตัวนั้น ๆ
ที่ผมทดลองทำดูเองนั้น ผมมีบอร์ด ET-EASYMEGA1280 อยู่ตัวหนึ่งและมีไอซี Atmega8 อยู่ตัวหนึ่ง

ทำให้บอร์ด Arduino เป็นตัวโปรแกรม
ในโปรแกรม Arduino IDE รุ่นล่าสุด (1.0.1) จะมี Sketch ตัวอย่างชื่อ ArduinoISP.ino ติดตั้งมาแล้ว โดยอยู่ที่เมนูน File -> Examples -> ArduinoISP Sketch นี้จะทำหน้าทึ่ทำให้บอร์ด Arduino ของเราทำตัวเป็นตัวโปรแกรม
  • ให้เปิด Sketch ArduinoISP ขึ้นมา
  • ให้เลือกบอร์ดจากเมนู Tools -> Board ให้ตรงกับบอร์ดที่เราจะใช้เป็นตัวโปรแกรม ผมจะใช้ ET-EASYMEGA1280 ก็เลือกบอร์ดให้เป็น Arduino Mega
  • สั่งอัพโหลด Sketch
Arduino IDE จะคอมไพล์ Sketch และอัพโหลด ArduinoISP ขึ้นไปที่บอร์ด Arduino เมื่อเสร็จสิ้นบอร์ด Arduino ของเราก็จะกลายเป็นตัวโปรแกรมแล้ว
 

ใช้บอร์ด Arduino เป็น ISP Programmer
หลังจากอัพโหลดเสร็จ บอร์ดของเราก็จะเป็น ISP Programmer แล้ว เราสามารถติดตั้ง Sketch ของเราลงบนไอซีไมโครคอนโทรลเลอร์ได้แล้ว การเชื่อมต่อไอซีกับบอร์ด Arduino ของเราก็ให้ดูตามซอร์สโค้ดของ ArduinoISP คือกรณีของผมซึ่งใช้ ET-EASYMEGA1280 ก็ให้ต่อ
  • ขา 53 ของบอร์ดเข้ากับขา Reset ของไอซี (ขา 1)
  • ขา 51 ของบอร์ดเข้ากับขา MOSI ของไอซี (ขา 17)
  • ขา 50 ของบอร์ดเข้ากับขา MISO ของไอซี (ขา 18)
  • ขา 53 ของบอร์ดเข้ากับขา SCK ของไอซี (ขา 19)
ต่อตัวต้านทานสัก 1 k ระหว่างขา Reset กับ Vcc นอกจากนี้อย่าลืมจ่าย Vcc และ Gnd ให้กับ AVcc และ AGnd ด้วย สุดท้ายใช้คริสตัล 16 MHz และตัวเก็บประจุขนาด 18 pF คู่หนึ่งเพื่อสร้างสัญญาณนาฬิกาให้กับไอซี เราสามารถใช้ Vcc และ Gnd ของบอร์ด Arduino จ่ายให้กับไอซี Atmega8 ได้


สมมติว่าเราจะทดลองติดตั้ง Sketch ชื่อ Blink จากตัวอย่างในโปรแกรม Arduino IDE ลงใน Atmega8 ก็ให้เปิด Sketch ตัวอย่างนั้นขึ้นมา
  • เลือกบอร์ดเป็น Arduino NG w/Atmega8 เพราะมีไอซึ Atmega8 อยู่
  • สั่ง File -> Upload using Programer หรือกด Crtl+Shift+U
Arduino IDE จะคอมไพล์และอัพโหลด Sketch ขึ้นไปบน Atmega8 ที่ต่อไว้ผ่าน ET-EASYMEGA1280 ของเรา

เพิ่มเติม
ข้อมูลจากเว็บ Arduino เองและจากแหล่งอื่น ๆ เช่น http://www.ayarafun.com/2009/04/upload-arduino-with-avrdude-gui/ ระบุตรงกันว่าสามารถอัพโหลดโปรแกรมได้โดยไม่ต้องมีบูตโหลดเดอร์ (Bootloader) ก็ได้

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

วิธีการติดตั้งบูตโหลดเดอร์
หลังจากที่ติดตั้งโปรแกรม ArduinoISP บนบอร์ด ET-EASYMEGA1280 แล้ว ต่อวงจรเพื่อติดตั้งบูตโหลดเดอร์เหมือนกับการอัพโหลดโปรแกรมเข้า Atmega8 ตามรูปข้างบนนั่นแหละ

หลังจากเลือกบอร์ดให้เป็น Arduino NG w/ Atmega8 (เพราะผมใช้ Atmega8) จากเมนู Tools -> Board แล้วก็เลือกตัวโปรแกรมเมอร์ให้เป็น Arduino as ISP จากเมนู Tools -> Programmer

เมื่อเสร็จก็สั่งติดตั้งบูตโหลดเดอร์ลงใน Atmega8 ด้วย Tools -> Burn Bootloader ก็เสร็จเรียบร้อยครับ หลังจากนี้จะติดตั้งโปรแกรมอะไรก็ทำได้เลย


แหล่งข้อมูล
http://arduino.cc/en/Tutorial/ArduinoISP
http://arduino.cc/en/Hacking/PinMapping
http://arduino.cc/en/Tutorial/ArduinoToBreadboard

วันพุธที่ 22 สิงหาคม พ.ศ. 2555

ดูภาพหน้าจอของ Android Tablet บน PC

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

Tablet เครื่องนี้มีข้อโดดเด่นอยู่อย่างหนึ่งคือมีช่องต่อพ่วงอุปกรณ์ครบ มี USB ใหญ่ USB เล็ก HDMI 3.5 mm Audio Jack SD Card ขาด VGA ก็จะใช้เป็นคอมพิวเตอร์เครื่องหนึ่งได้แล้ว

แต่คงเพราะเป็น Tablet จึงมีปุ่มควบคุมอยู่น้อย มีปุ่ม Volume ปุ่มเรียกเมนู และปุ่ม ESC แค่นี้ ที่เหลือต้องควบคุมผ่านหน้าจอระบบสัมผัสทั้งหมด

อะไรจะเกิดขึ้นถ้ามีอุบัติเหตุทำให้การตั้งค่าความสว่างหน้าจอเป็นศูนย์!??

จอมันก็มืดสิครับ ผมเคยเจอปัญหานี้หนหนึ่งเมื่อสักสองวันก่อนนี่เอง คือไปลงโปรแกรมประหยัดแบตเตอรีแล้วอ่าน UI ไม่เข้าใจไปตั้งค่าให้ประหยัดพลังงานโดยปิดหน้าจอ มืดสนิทไปหมด เราก็นึกว่าเครื่องดับเลย Reset มันซะ ปรากฏว่าเครื่องก็ Restart ตามปกตีมี Splash Screen มาตามลำดับ ๆ พอจบลำดับจอก็มืดเหมือนเดิม เพราะโปรแกรมประหยัดแบตเตอรีมันคุมไว้ ทำไงดีล่ะทีนี้

ก็เลยคิดว่ามันน่าจะมีการควบคุม Android Tablet ผ่าน PC ได้สักทางล่ะน่า หลังจากค้นหาอยู่ไม่นาน (Thank you Google!) ก็พบหน้าเว็บนี้ครับ How to Remote View and Control Your Android Phone ซึ่งอธิบายว่าเราสามารถดูภาพหน้าจอของ Android Tablet ได้โดยใช้เครื่องมือ 3 อย่างคือ
  1. โปรแกรม ADB ซึ่งได้จาก Android SDK อยู่ในไดเรคทอรีย่อย platform-tools
  2. สาย USB
  3. โปรแกรม Androidscreencast ซึ่งเป็นโปรแกรมภาษาจาวา
ขั้นตอนก็ขอเรียบเรียงเป็น 2 ส่วนนะครับ คือส่วนเตรียมการและส่วนใช้งาน ส่วนเตรียมการคือทำหนเดียวแล้วไม่ต้องทำอีก ส่วนใช้งานก็คือทำทุกครั้งที่ใช้งาน

ส่วนเตรียมการ
  • ติดตั้ง JAVA JRE เวอร์ชัน 5 ขึ้นไป ถ้ายังไม่มี (แต่น่าจะมีกันหมดแล้วล่ะมั้ง) เอาไว้รันโปรแกรม Androidscreencast
  • ติดตั้ง Android SDK รุ่นล่าสุด เพื่อจะได้ใช้ ADB ได้
  • ติดตั้งโปรแกรม Androidscreencast โดยการดาวน์โหลดโปรแกรมมาแล้วเก็บไว้ที่ใดสักที่หนึ่งที่ไม่เกะกะ ตรงไหนก็ได้ครับผมเก็บไว้ใน platform-tools นั่นแหละ ไม่ต้อง Setup ไม่ต้อง Install
สำหรับ Debian และ Ubuntu อาจารย์รุจชัยเคยมาบรรยายที่ KKLUG ว่าจะต้องผูกให้ระบบรู้จักกับ Tablet เสียก่อนโดย
สร้างแฟ้มข้อความ (Text File) ชื่อ 52-android.rules ไว้ใน /etc/udev/rules.d/ (แน่นอนว่าต้องผ่าน su นะครับ) โดยในแฟ้มจะมีข้อความว่า
SUBSYSTEM=="usb",SYSFS{idVendor}=="xxxx",ATTRS{idProduct}="yyyy",MODE="666"
ทั้งนี้ xxxx คือ Vendor ID และ yyyy คือ Product ID ที่สามารถทราบได้โดยใช้คำสั่ง
lsusb
ซึ่งจะแสดงรายการอุปกรณ์ที่ต่อพ่วงกับ PC ของเราทาง USB ดังนี้
Bus zzz Device zzz: ID xxxx:yyyy Product Name
เมื่อสร้างแฟ้มข้อความ 52-android.rules แล้วตรวจให้แน่ใจว่าโหมดของแฟ้มคือ 644 หลังจากนั้นเพื่อให้การเปลี่ยนแปลงเกิดผลทันทีก็สั่งให้บริการ ABD เริ่มต้นใหม่โดย
> ./adb kill-server
> ./adb start-server
มาถึงจุดนี้ก็ถือว่าพร้อมใช้งาน ADB ผ่านพอร์ด USB แล้ว

ส่วนใช้งาน
  • ต่อ Android Tablet เข้ากับพอร์ด USB ของ PC ตอนนี้ Tablet อาจจะถามว่าจะเปิดที่เก็บข้อมูลไหม ไม่ต้องตอบครับ Exit ออกมาเลย ถ้าเปิดที่เก็บข้อมูล PC จะมองเห็น Tablet เราเป็น USB Flashdrive ตัวหนึ่งเท่านั้น ถ้าทำต่อมาจากขั้นเตรียมการก็ข้ามขั้นนี้ไปเลย
  • เรียกใช้โปรแกรม ADB ซึ่งใน Debian ของผมจะใช้ Command Line
> ./adb devices
ถ้าเชื่อมต่อได้ก็จะมีข้อความขึ้นมาที่ Prompt ดังนี้
* daemon not running. starting it now on port xxxx *
* daemon started successfully *
List of devices attached
xxxxxxxxxxxxxxxx device
  • เรียกใช้โปรแกรม androidscreencast.jnlp
> jawaws androidscreencast.jnlp
เราจะเห็นหน้าจอของ Tablet ของเราอยู่ในหน้าต่างของโปรแกรม androidscreencast.jnlp นี่แหละครับ  จะบันทึกเป็นวิดีโอก็ได้ ในระบบของผมเขาจะบันทึกให้เป็นแฟ้ม MOV ดังตัวอย่างข้างล่างนี้ครับ

หรือจะบันทึกภาพหน้าจอก็ใช้เครื่องมือบันทึกภาพหน้าจอของ Gnome ก็ได้

เมื่อเราเห็นหน้าจอของ Tablet ของเราทางจอคอมพิวเตอร์ (ตอนนี้จอ Tablet มันดำอยู่) ก็พอจะหาทางควบคุมความสว่างได้ แต่ขอบอกให้ชัด ๆ ก่อนว่าไม่ใช่ว่าควบคุม Tablet จาก PC แต่ให้ PC แสดงหน้าจอของ Tablet เฉย ๆ เรายังต้องใช้ Touch Screen ของ Tablet ในการควบคุมอยู่ดี

ในเว็บแจ้งว่าหากต้องการควบคุม Tablet ผ่าน PC แบบเต็มตัวจะต้อง Root Tablet ตัวนั้นเสียก่อน ผมยังไม่กล้าพอครับ

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

วันเสาร์ที่ 28 กรกฎาคม พ.ศ. 2555

เน็ต 3BB กับ Debian

ใช้ 3BB มาหลายเดือนแล้ว ใช้งานได้ดีไม่มีปัญหา แต่.....

อันที่จริงแล้วผมมีปัญหากับการเข้าถึงอินเตอร์เนตผ่าน 3BB ตั้งแต่วันแรก นั่นคือเข้าเว็บไม่ได้ แต่เมื่อตอนนั้นคิดว่าคงเป็นปัญหาเรื่อง DNS ก็เลยไปใช้ Google DNS คือ 8.8.4.4 และ 8.8.8.8 แทน ซึ่งก็ใช้ได้ดีมาตลอดจนกระทั่งเมื่อ 2 - 3 วันมานี้จู่ ๆ ก็เข้าเว็บบางเว็บไม่ได้!


บางเว็บที่เข้าได้คือ google.com และเว็บต่างประเทศบางเว็บ ส่วนเว็บที่เข้าไม่ได้ส่วนใหญ่เป็นเว็บในไทย ตั้งแต่ sanook.com ไปจนถึง 3bb.co.th เอง ยกเว้น pantip.com เอาไงดีล่ะเรา...

มีคนบอกว่าให้เปลี่ยนกลับมาใช้ DNS ของ 3BB ก็ลองเปลี่ยนกลับดู แล้วลองใช้ dig หรือ nslookup ตรวจสอบดูว่า DNS ทำงานได้หรือไม่ ก็ปรากฏว่า DNS ไม่ว่าจะเป็นของ Google หรือของ 3BB ก็ทำงานได้ดี คือแสดงเลขไอพีของเว็บนั้น ๆ ได้ทุกเว็บ ไม่มีปัญหาเลยสักเว็บเดียว แสดงว่าปัญหาไม่ใช่ DNS แล้ว

พอได้เลขไอพีมาก็ลอง ping ดู ปรากฏว่าไม่เวิร์ก ฝ่ายเทคนิคของ 3BB บอกว่า ping ไม่เวิร์กเพราะหลายเว็บเขาบล๊อก ping เราก็เลยใช้ ping ตรวจสอบไม่ได้ แต่พอดีทราบว่าเว็บของภาควิชาฯ ไม่ได้บล๊อก ping ก็เลยลอง ping ดู ก็ปรากฏว่าเจอ แต่ก็เข้าเว็บภาควิชาไม่ได้

เอ...หรือว่าเรามีปัญหาการสื่อสาร ลอง ftp เข้าไปที่เซิฟเวอร์ของภาควิชาสิ...ก็เข้าได้ ลองเข้าเว็บภาควิชาสิ...เข้าไม่ได้แฮะ

มันไปติดที่ไหนนะ? สงสัยก็เลยใช้ traceroute ติดตามการสื่อสารดูว่ามันไปติดที่ไหน ปรากฏว่ามันก็ออกไปจนถึง 3bb นั่นแหละ แล้วก็ติดแหงกอยู่ที่นั่น ว่าแต่ว่าทำไมล่ะ ทำไมบางเว็บเข้าได้บางเว็บเข้าไม่ได้ และยืนยันแล้วว่าไม่ใช่ปัญหา DNS ด้วย

คิดไปคิดมาเห็นในหน้าต่าง Interface Config ว่ามีตัวเลือกของ IPv6 คิดว่ายังไม่น่าจะได้ใช้ก็ Disable มันซะ ลองเข้าเว็บอีกที...ยังไม่เวิร์ก แต่สัญชาตญาณบอกว่ามันน่าจะเป็นเรื่อง IPv6 นี่แหละ

ในเมื่อ DNS ไม่ใช่ปัญหา การเชื่อมต่อไม่ใช่ปัญหา ftp ได้ ปัญหาอาจอยู่ที่เบราเซอร์ เลยลองค้นคีย์เวิร์ด Firefox กับ IPv6 ดูจึงได้พบว่ามีหลายคนในโลกเจอปัญหาเข้าเว็บบางเว็บไม่ได้เหมือนกัน และทางแก้คือ Disable IPv6 ใน Firefox ซะ เอาล่ะ ดูจะมีความหวังอยู่บ้าง

วิธีการคือ พิมพ์ about:config ลงในช่อง URL Firefox จะเตือนเราก่อนว่าการแก้ไขตรงนี้อาจทำให้ Firefox ทำงานผิดปกติได้ เราก็รับคำเตือนนั้นเสีย แล้วค้นหา Field ชื่อ network.dns.disableIPv6 โดยใช้ Filter ที่ต้องใช้ Filter เพราะ Field มันเยอะมาก และเราก็ไม่อยากพลั้งพลาดไปเปลี่ยนค่าสำคัญอื่น ๆ โดยไม่ตั้งใจ เมื่อค้นเจอ network.dns.disableIPv6 แล้วก็ตั้งค่าให้เป็น TRUE เสีย (เดิมเป็น FALSE)

ปรากฏว่าใช้ได้ครับ! เข้าเว็บทุกเว็บได้ตามปกติเลย เนื่องจากเคยได้ยินว่ามีหลายคนมีปัญหาคล้าย ๆ กันอย่างนี้จึงขอบันทึกไว้เพื่อเป็นประโยชน์ในภายหน้า

สรุปว่าปัญหาอย่างนี้ทำให้ประสบการณ์การใช้ Linux มากขึ้น ได้เรียนรู้มากขึ้น คำสั่งต่าง ๆ ทั้ง dig nslookup ping traceroute แม้กระทั่ง route table ซึ่งก่อนหน้านี้ไม่รู้จัก ไม่ใด้ใช้เลยก็ได้ค่อย ๆ ทำความรู้จักกับมันขึ้นมา ทำให้มีความเข้าใจระบบเครือข่ายมากขึ้น ก็คงเหมือนอย่างภาษิตโบราณเขาว่า มารบ่มี บารมีบ่เกิด กระมัง

วันเสาร์ที่ 19 พฤษภาคม พ.ศ. 2555

การใช้ Calc ตัดเกรดนักศึกษา

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

ผมยอนใจอ่อนสอนเองไม่ตำหนิว่าเป็นความผิดของนักเรียนหรอก แต่เอาเถอะ บันทึกไว้เสียหน่อยก็แล้วกันว่าจะสอนยอมภาคฤดูร้อนเป็นครั้งสุดท้ายแล้ว อย่างไรเสียแม้ว่าการสอนจะเป็นความรับผิดชอบของเรา แต่การเรียนก็เป็นความรับผิดชอบส่วนตนของนักศึกษา จะเรียนตกเรียนหล่นในเทอมปกติแล้วจะมาจับตัวเองเป็นตัวประกันในเทอมฤดูร้อนนั้นไม่ได้ และการสอนภาคฤดูร้อนก็ไม่ใช่หน้าที่รับผิดชอบของอาจารย์นะ (มหาวิทยาลัยกำหนดงานอาจารย์ในช่วงนี้ไว้ให้แล้วครับ - ไม่ใช่ว่าจะว่าง) งานของใครก็งานของมันนะครับนักเรียน รับผิดชอบงานในส่วนของตัวเองด้วย

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

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

ทำตารางคะแนนของนักเรียนขึ้นมาเสียก่อน จะมีคะแนนเท่าใด สอบเท่าใดก็ว่ากันไป แต่สุดท้ายให้รวมให้ได้ 100 คะแนนเต็ม ดังตัวอย่าง
จากนั้นทำตารางช่วงคะแนนสำหรับแต่ละเกรดไว้ที่ใดที่หนึ่ง อาจจะต่อท้ายตารางคะแนนหรือจะเอาไว้อีกแผ่นงาน (Sheet) หนึ่งเลยก็ได้ แต่สิ่งสำคัญคือเกรดต้องเรียงจากน้อยไปหามาก ให้คอลัมน์ซ้ายมือเป็นคะแนนต่ำสุดที่จะได้เกรดนั้น ๆ และคอลัมน์ขวาเป็นเกรดนั้น ๆ ดังรูป
ไปที่ เกรด แถวแรก (ในตัวอย่างคือเซล r2) ใช้ฟังก์ชัน vlookup เพื่อเทียบช่วงคะแนน
ซึ่งเราสามารถเขียนเป็นฟังก์ชันลงในเซล R2 โดยตรงก็ได้ดังนี้
=vlookup(i2,$S$2:$T$10,2,1)
  • i2 นั้นหมายถึงจะร้อยละของคะแนนจากเซล i2 เป็นตัวค้นหา 
  • $s$2:$T$9  หมายถึงตารางเกรด การที่มีเครื่องหมายดอลลาร์อยู่หน้า S และ 2 หมายถึงเราต้องการจะอ้างอิงตำแหน่งนี้แบบสัมบูรณ์ (Absolute) ไม่ใช่แบบสัมพัทธ์ (Relative) ซึ่งเป็นค่าปริยายของการอ้างถึงตำแหน่ง
  • 2 คือเลขคอลัมน์ในตารางเกรดที่จะใช้เป็นค่าส่งคืนของฟังก์ชัน vlookup เมื่อคะแนนใน i2 พบเกณฑ์ของตนเองในตาราง $s$2:$T$9 แล้วจะนำค่าในคอลัมน์นี้ (คือคอลัมน์ที่ 2) ของตารางส่งคืนค่ามาให้ Calc
  • 1 ในอาร์กิวเมนต์สุดท้ายเป็นการระบุว่าตารางเกรด $s$2:$T$9 ของเรานี้มีการเรียงค่าจะน้อยไปหามาก ข้อสุดท้ายนี้จำเป็นเนื่องจากคะแนนอาจมีหลากหลายค่า ไม่ตรงกันกับค่าในตาราง กระบวนการค้นหาจะไล่ค้นหาจากน้อยไปหามาก ก็คือหาจากข้างบนลงข้างล่างนั่นเองและจะคืนค่าที่น้อยกว่าที่ใกล้ที่สุดให้
เมื่อได้เกรดในเซล r2 แล้วก็สามารถคัดลอกสูตรในเซลนี้ไปยังแถวอื่น ๆ ได้
จะขออธิบายการทำงานของฟังก์ชันนี้โดยใช้ตัวอย่างคะแนนในเซล i3 มีค่าเท่ากับ 57.50
  • เกณฑ์ค้นหา (อาร์กิวเมนต์ที่ 1) คือ 57.50
  • ตารางที่ค้นหา (อาร์กิวเมนต์ที่ 2) คือ s2:t9 ถ้าไม่อ้างตำแหน่งแบบสัมบูรณ์ในเซล r2 เมื่อคัดลอกสูตรมาที่ r3 ตารางจะกลายเป็น r3:t10 ซึ่งไม่ถูกต้อง
  • ค่าที่จะเป็นเกรดอยู่ในคอลัมน์ที่ 2 ของตาราง เลข 2 นี้ก็คืออาร์กิวเมนต์ที่ 3 ของฟังก์ชัน
  • ค่าที่จะค้นคือ 57.5 การค้นจะไล่จากบนลงล่าง 
    • ค้นแถวแรกเจอ 0 และ 57.5 มากกว่า 0 vlookup จะเก็บค่า F ไว้
    • ค้นแถวที่สองเจอ 45 และ 57.5 มากกว่า 45 vlookup จะเก็บค่า D ไว้
    • ค้นแถวที่สามเจอ 50 และ 57.5 มากกว่า 50 vlookup จะเก็บค่า D+ ไว้
    • ค้นแถวที่สี่เจอ 55 และ 57.5 มากกว่า 55 vlookup จะเก็บค่า C ไว้
    • ค้นแถวที่ห้าเจอ 60 แต่ 57.5 น้อยกว่า 60 vlookup ก็เลยบอกว่าหาไม่เจอแล้ว เอาค่าล่าสุดไปก่อนก็แล้วกัน เราก็จะได้เกรด C คืนกลับมาในเซล r3
อันที่จริงจากการลองผิดลองถูกเอง ผมได้ทดลองใช้ฟังก์ชัน if ซ้อน if ถึง 7 ชั้นด้วยกัน ซึ่งก็สามารถใช้งานได้แต่จะผิดพลาดได้ง่ายมาก เลยยังไม่ค่อยพอใจเท่าไร หลังจากนั้นจึงได้พบกับตัวอย่างของ vlookup ในภายหลัง แม้ว่าจะมีหลายคนทำเป็นวิดีโอและเว็บแล้ว ผมก็อยากจะบันทึกเก็บไว้เป็นอีกเสียงหนึ่ง เผื่อว่ามีใครหาคำว่า "ใช้ calc ตัดเกรด" จะได้หาข้อมูลเจอได้เร็วและไม่ต้องเสียเวลาไปเหมือนผมอีก
 
หมายเหตุ
  1. เซลในตัวอย่างกระโดดจาก i ไป r เพราะผมมีบางเซลที่ซ่อนไว้ไม่เกี่ยวกับงานนี้ครับ
  2. ในโปรแกรมจำพวกกระดานคำนวณ (Spreadsheet) อื่น ๆ ก็ทำได้เช่นเดียวกันนะครับ วิธีก็น่าจะคล้าย ๆ กันนั่นแหละ เดี๋ยวจะมาหาว่าผมเชียร์ LibreOffice จนออกนอกหน้า

วันพฤหัสบดีที่ 1 มีนาคม พ.ศ. 2555

หัดเขียนแอนดรอยด์ - โปรแกรมฆ้องวง

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

แต่เมื่อปลายปีที่ผ่านมามีโอกาสได้จับแทบเบล็ตของเพื่อน ลองเล่นดูทำให้มองเห็นว่าที่มีคนกล่าวกว่า Mobile Computing is already here น่าจะเป็นจริง ก็เลยคิดว่าควรหาความรู้ด้านนี้ใส่ตัวไว้บ้าง

หลายปีก่อนผมเคยพยายามเรียนรู้ Java แต่ไม่ประสบความสำเร็จเนื่องจากไม่มีความสามารถที่จะทำความเข้าใจระบบคลาสที่ซับซ้อนของ Java ได้ ศาสตร์ของ Java เองก็ถือว่ากว้างขวางจนกระทั่งเมื่อเริ่มเรียนรู้ขั้นต้นแล้วแล้วจะไปต่อขั้นกลาง ขั้นสูงก็ไปไม่ถูก (แต่ก็ได้พื้นฐานของ Java มาเล็กน้อย พออ่านโปรแกรมออก)

ในทางกลับกัน ถึง Android จะใช้พื้นฐานภาษา Java (ซึ่งผมขยาดเอาเลยทีเดียว) แต่ระบบคลาสของ Android ชัดเจนกว่ามาก ทำให้เกิดทัศนคติต่อตัวเองว่าการเรียนรู้ Android น่าจะง่ายกว่า ซึ่งหลังจากที่ลองลงมือทำก็พบว่าจริงทีเดียว

แม้ว่า Android จะเป็น Open Source แต่การติดตั้ง SDK และ IDE ลงบน Debian กลับเป็นเรื่องไม่ง่ายนัก คือใช้ apt-get หรือ synaptic ก็ไม่ได้ นี่เป็นอุปสรรคสำหรับผมอย่างมากจนกระทั่งผมได้พบกับ Developing Android Applications on Debain ที่ workaround.org ซึ่งมีคำแนะนำอย่างละเอียด

เมื่อดำเนินการตามขั้นตอนที่แนะนำแล้วก็เริ่มต้นเรียนรู้ Android จาก Hello, World พอเริ่มเข้าใจเครื่องไม้เครื่องมือและมองเห็นศักยภาพของ Android แล้วก็เกิดไอเดียบรรเจิดว่าอย่างทำโน่นอยากทำนี่ สิ่งแรกที่อยากทำบนแอนดรอยด์คือโปรแกรมดนตรีไทย เริ่มต้นที่ฆ้องวงก่อน เพราะมีทรัพยากรครบ เสียงต้นแบบก็มีแล้ว ภาพลูกฆ้องก็มีแล้ว กลไกการสังเคราะห์ก็มีแล้ว

โปรแกรมฆ้องวงรุ่นแรกสุดใช้ ImageButton เป็นรูปฆ้อง ใช้ Absolute Layout ระบุตำแหน่งลงใน XML และใช้ MediaPlayer เพื่อเล่นเสียงฆ้องที่บันทึกไว้ล่วงหน้า (ไม่ได้สังเคราะห์) โปรดสังเกตว่าคลาสชื่อ HelloButtonActivity ที่เป็นเช่นนี้เพราะว่าโปรแกรมนี้เริ่มมาจากการศึกษาโปรแกรม HelloButton ของ Android Tutorial บทแรก ๆ แล้วค่อย ๆ ขยายขอบเขตออกมา (โปรแกรมที่แสดงนี้ลดรูป คือเหลือลูกฆ้องเพียงลูกเดียวเท่านั้น)
public class HelloButtonActivity extends Activity {

        ImageButton gongButton01;
 
        OnClickListener gongListener = new OnClickListener() {
         @Override
  public void onClick(View v){
          if (v==gongButton01)
   {
           MediaPlayer mp = MediaPlayer.create(HelloButtonActivity.this, R.raw.gong01);
           mp.setOnCompletionListener(new OnCompletionListener() {
                                 public void onCompletion(MediaPlayer mp) {                
                                         mp.stop();
                                         mp.release();
                                 }
    });
    mp.start(); 
      }
         }

         @Override
 public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
        
                setContentView(R.layout.main);

                addListenerOnButton();
        }

        public void addListenerOnButton(){
             gongButton01 = (ImageButton) findViewById(R.id.gongButton01);
        }
}

วิธีแบบนี้มีข้อจำกัดอยู่หลายประการคือ
  1. เมื่อเป็น ImageButton แล้ว Event ที่ตรวจจับได้คือ Click ซึ่งหมายความว่าต้อง "กด" และ "ปล่อย" จึงจะนับเป็น Click Event และโปรแกรมจะส่งเสียงออกมา ในมุมมองของผู้ใช้ถือว่าไม่สมจริง
  2. วิศวกรของ Android ไม่แนะนำให้ใช้ Absolute Layout เนื่องจากหน้าจอขอโทรศัพท์มีหลายขนาด
  3. MediaPlayer ที่เล่นเสียงของแต่ละลูกฆ้องถือเป็น 1 อินสแตนซ์ เรียกมาเล่นแล้วต้องลบทิ้ง ไม่เช่นนั้นแล้วโปรแกรมจะกินพื้นที่หน่วยความจำมากขึ้นเรื่อย ๆ และวิธีที่ผมใช้เขียนทำให้เล่นได้ทีละ 1 เสียงเท่านั้น ในทางปฏิบัติการบรรเลงฆ้องจะใช้สองมือ ควรเล่นได้พร้อมกันอย่างน้อย 2 เสียง โปรแกรมเมอร์หลายคนแนะนำว่าให้ใช้ SoundPool ดีกว่า เพราะ SoundPool 1 อินสแตนซ์เรียกเสียงมาเล่นได้พร้อม ๆ กันหลายเสียง
หลังจากนั้นได้มีโอกาสชมผลงานโครงงานนักศึกษาภาควิชาวิศวกรรมคอมพิวเตอร์ มีหลายโครงงานใช้แอนดรอยด์ เลยได้เข้าไปสอบถามหาความรู้เพิ่มเติม นักศึกษาแนะนำว่าใช้ Canvas ดีกว่า ตอนนั้นยังไม่รู้ว่า Canvas คืออะไร ก็มาถามเฮียกู เฮียเลยแนะนำให้รู้จักกับ Android Tutorial: 2D Canvas Graphics ที่ Programmer XR พบว่าการวาดรูปลง Canvas นั้นไม่ยาก ดูจากตัวอย่างแล้วดัดแปลงนิด ๆ หน่อย ๆ ก็ใช้ได้ ไอ้ที่ยากน่ะคือให้รูปใน Canvas มันดักอีเวนต์การแตะหน้าจอ (Tap) ต่างหาก

จากการดู Source Code ของชาวบ้านประกอบกับคำอธิบายใน stackoverflow (นี่แหละข้อดีของโอเพนซอร์ส) ถึงได้รู้ว่าเขาใช้คลาส onTouch แล้วดักด้วย OnTouchListener กัน แต่การทำแบบนี้จะถือว่าฆ้องทั้ง 16 ลูกเป็นรูปภาพเพียงรูปเดียว ดัก OnTouchListener ฟังก์ชันเดียว แล้วจำแนกการเล่นเสียงของฆ้องแต่ละลูกตามตำแหน่งสัมบูรณ์ใน Canvas

พอดัก OnTouchListener ได้แล้วก็กำหนดให้โปรแกรมเรียก SoundPool มาเล่นเสียงที่บันทึกไว้ล่วงหน้าเมื่อเกิด onTouch ขึ้น การใช้ SoundPool นี้ได้ข้อมูลจาก The Game Programming Wiki ลดรูปตัดมาใช้เฉพาะที่จำเป็น (โปรแกรมที่แสดงนี้ลดรูปเช่นเดียวกัน คือเหลือลูกฆ้องเพียงลูกเดียว)
public class GongwongActivity extends Activity {
        SoundPool soundPool;
 int gong01ID; 
 
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                requestWindowFeature(Window.FEATURE_NO_TITLE);        
                setContentView(new Panel(this));
                setVolumeControlStream(AudioManager.STREAM_MUSIC);
                soundPool = new SoundPool(16, AudioManager.STREAM_MUSIC,0);            
  gong01ID = soundPool.load(getBaseContext(), R.raw.gong01, 1);
    }
    
    class Panel extends View {
     Rect dst = new Rect();
     float xTouch, yTouch;
     
     public Panel(Context context) {
      super(context);
     }
     
     public boolean onTouchEvent(MotionEvent event){
      int action = event.getAction();
      
      switch(action){
      case MotionEvent.ACTION_DOWN:
       xTouch = event.getX();
       yTouch = event.getY();
       if (xTouch > 44 & xTouch < 44 + 68 & yTouch > 224 & yTouch < 224 + 68){
        soundPool.play(gong01ID,1,1,0,0,1);
       }
      }
      return true;
     }
     
     @Override
     public void onDraw(Canvas canvas) {
      dst.set(0,0,480,800);
      Bitmap gongwong = BitmapFactory.decodeResource(getResources(), R.drawable.gongimg);
      canvas.drawColor(Color.BLACK);
      canvas.drawBitmap(gongwong, null, dst,null);
     }
    }
}


ผลลัพธ์ก็คือโปรแกรม gongwong โดย SDK ที่ใช้เป็นรุ่น Android 2.0 ไม่น่ามีปัญหากับอุปกรณ์แอนดรอยด์ส่วนใหญ่ หากท่านในสนใจทดลองใช้งาน ก็สามารถดาวน์โหลดแฟ้ม APK ได้ที่นี่ (ขอเตือนก่อนว่าโปรแกรมรุ่นนี้ถือเป็น Alpha Version อาจมีข้อบกพร่องอยู่มาก) เมื่อเรียกใช้โปรแกรมบนแทบเบล็ตก็จะเห็นหน้าตาโปรแกรมดังรูป และเมื่อแตะบนหน้าจอในบริเวณลูกฆ้องก็จะได้ยินเสียงฆ้องแต่ละลูก ทั้งนี้จากการทดลองพบว่าหูฟังจะให้คุณภาพเสียงที่ดีกว่าลำโพงก็ขอให้แนะนำให้ใช้กับหูฟังก็แล้วกัน

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

สิ่งที่ต้องปรับปรุง
  1. แม้ว่า SoundPool จะเล่นเสียงได้พร้อมกันหลายเสียง แต่ onTouch ที่ตรวจจับได้ก็จับได้ทีละ Touch ยังไม่สามารถขยายไปถึง Multi Touch ได้
  2. รูปฆ้องสำหรับฆ้องลูกเล็ก เล็กเกินไปโดยแม้กระทั่งกับแทบเบล็ตที่มีหน้าจอขนาด 8 นิ้ว รูปลูกฆ้องไม่จำเป็นต้องย่อส่วนตามมาตรส่วนคงที่ก็ได้
  3. การกำหนดขอบเขตของฆ้องแต่ละลูก ใช้การกำหนดค่าคงที่ตายตัวลงในโปรแกรม นี่ไม่ใช่การเขียนโปรแกรมที่ดี ควรหาทางจัดเก็บข้อมูลตามรูปแบบมาตรฐาน หรือไม่เช่นนั้นก็ให้หันไปใช้แนวทางให้ลูกฆ้องแต่ละลูกเป็น Sprite ที่แต่ละตัวมี OnTouchListener ของตัวเองก็ได้ ถ้าเป็นอย่างนี้ ก็ไม่ต้องกำหนดขอบเขตลูกฆ้องแต่ต้องกำหนดตำแหน่งแทน
  4. หากฟังก์ชันพื้นฐานสมบูรณ์ดีแล้ว ควรทดสอบโปรเจ็คตามกรรมวิธีมาตรฐานของ Android ด้วย (ยังทำไม่เป็น)

วันศุกร์ที่ 24 กุมภาพันธ์ พ.ศ. 2555

การเขียนลายวงจร (Schematic) สำหรับเอกสาร LaTeX


ในเอกสารประกอบการสอนของผม จำเป็นต้องใช้ลายวงจร (Schematic) อยู่บ่อย ๆ ทั้งในวิชาวงจรไฟฟ้า (Electric Circuits) และวิชาวิศวกรรมอิเล็กทรอนิกส์ (Engineering Electronics) แต่เดิมจะใช้ xfig วาดรูปวงจรและเก็บแฟ้มเอกสารเป็น EPS

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

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


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

\begin{circuitikz}
\draw
(0,0) node[op amp,yscale=-1] (opamp) {}
(opamp.+) node {} -- (-3,0.5)
(-3,-1) node[ground] {} to[american voltage source,l=$V_{i}$] (-3,0.5)
(opamp.out) node {} to[short,-o] (2,0) node[anchor=west] {$V_{o}$}
(1.5,0) to[R,l=$R_2$,*-*] (1.5,-2)
to[R,l=$R_{1}$,*-] (1.5,-4) node[ground] {}
(1.5,-2) node[] {} -| (opamp.-) node {}
;
\end{circuitikz}

สคริปต์ของ circuitikz จะอยู่ใน Environment ชื่อ circuitikz คืออยู่ระหว่าง \begin{circuitikz}และ \end{circuitikz} สำหรับสคริปต์ในตัวอย่างนี้มีคำสั่งเดียวคือ \draw ซึ่งอยู่ที่บรรทัดที่สอง (บรรทัดแรกคือ Environment)

บรรทัดที่ 3 (0,0) node[op amp, yscale=-1] (opamp) {} คือการวางสัญลักษณ์ออปแอมป์ (Op-Amp) ลงที่ตำแหน่ง (0,0) พลิกสัญลักษณ์ในแกน y (yscale=-1) ตั้งชื่อมันว่า opamp แต่ไม่ต้องแสดงข้อความอะไรบนสัญลักษณ์

บรรทัดที่ 4 (opamp.+) node {} -- (-3,0.5) เป็นการอ้างถึงโหนด (+) ของออปแอมป์ และลากเส้นตรงไปที่ตำแหน่ง (-3,0.5)

บรรทัดที่ 5 (-3,-1) node[ground] {} to[american voltage source,l=$V_{i}$] (-3,0.5) เป็นการสั่งให้วาดโหนดกราวด์ลงไปที่ตำแหน่ง (-3,-1) แต่ไม่ต้องแสดงข้อความอะไรลงที่โหนดนี้ และจากโหนดกราวด์นี้จะเชื่อมกับตำแหน่ง (-3,0.5) ผ่านแหล่งจ่ายแรงดันที่มีฉลาก (Label) เป็น $V_{i}$

บรรทัดที่ 6 (opamp.out) node {} to[short,-o] (2,0) node[anchor=west] {$V_{o}$} จากโหนดเอาต์พุตของออปแอมป์ ให้ลากเส้นตรง ปลายทางเป็นจุดดำโปร่ง [Short,-o] ไปถึงจุด (2,0) แสดงข้อความประจำโหนดเป็น $V_{o}$ โดยให้โหนด (2,0) อยู่ทางซ้ายของข้อความ [anchor=west]

บรรทัดที่ 7 (1.5,0) to[R,l=$R_2$,*-*] (1.5,-2) เชื่อมตำแหน่ง (1.5,0) กับ (1.5,-2) ด้วยตัวต้านทานที่มีฉลากเป็น $R_2$ และให้ปลายทั้งสองข้างของตัวต้านทานเป็นโหนดทึบ

บรรทัดที่ 8 to[R,l=$R_1$,*-] (1.5,-4) node[ground] {} ให้เชื่อมตำแหน่งล่าสุดจากบรรทัดก่อนหน้าซึ่งก็คือ (1.5,-2) กับตำแหน่ง (1.5,-4) ด้วยตัวต้านทานที่มีฉลากเป็น $R_1$ กำหนดให้ตำแหน่งต้นทางเป็นโหนดทึบ ส่วนตำแหน่งปลายทางเป็นสัญลักษณ์กราวด์

บรรทัดที่ 9 (1.5,-2) node[] {} -| (opamp.-) node {} ให้ลากเส้นจากตำแหน่ง (1.5,-2) ไปที่โหนด (-) ของออปแอมป์ โดยใช้เส้นฉาก

สำหรับบรรทัดสุดท้ายคือเครื่องหมายเซมิโคลอนเป็นการสิ้นสุดคำสั่ง \draw

หลายคนอาจจะสงสัยว่าจะวาดลายวงจรเพียงเท่านี้จะต้องเขียนสคริปต์ขนาดนี้มันจะไม่มากไปหน่อยหรือ จากประสบการณ์ของผมเองผมพบว่าหากเป็นวงจรไม่ใหญ่มาก การเปิดโปรแกรมวาดลายวงจรเช่น xfig ดูจะเป็นภาระมากไปหน่อย หากใช้ circuitikz จนคุ้นเคยแล้ว การเขียนสคริปต์สัก 20 -- 30 บรรทัดถือว่าเป็น Inline Coding ที่สะดวกทีเดียว