สร้าง Ring Tone ให้กับ Raspberry Pi ผ่าน GPIO

กล่าวถึง ring tone หลายคนคงต้องโยงเข้าหาสองเรื่องคือเพลงและโทรศัพท์มือถือ แต่ในบทความนี้จะโยงเข้าหา Raspberry Pi แทนโทรศัพท์ ครับ

Ring tone เป็นเสียงที่สร้างขึ้นด้วยโทรศัพท์เพื่อเป็นสัญญาณแจ้งให้ทราบว่ามีสายเรียกหรือข้อความเข้ามายังโทรศัพท์ เดิมที่เดียวเสียง ring tone จะเป็นกริ่ง (Monophonic) เหมือนกับเราเล่นดนตรีด้วยโน๊ตเดียวตลอด แต่มาก็เริ่มนำเอาเสียงเพลง (Polyphonic) มาใช้แทนซึ่งนำมาซึ่งความเพลิดเพลินมากขึ้นเพราะเล่นได้หลายโน๊ตและมีทำนอง

การสร้าง ring tone ก็อาศัยพื้นฐานเดียวกับการสร้างเพลงด้วยระบบดิจิตอลทั่วไปเพื่อให้มีไฟล์ของเสียงเพลงที่จะนำมาใช้เป็น ring tone ก่อน แล้วค่อยส่งไปให้โทรศัพท์ทำการเล่นอีกทีแบบเดียวกับการดาวน์โหลดไฟล์ทั่วไป แต่มีบริษัท Nokia ที่คิดวิธีการส่ง ring tone ไปยังโทรศัพท์แบบการส่งข้อความ โดยการเปลี่ยนทำนองเพลงให้อยู่ในรูปแบบข้อความที่เรียกว่า Ring Tone Text Transfer Language (RTTTL) ก่อนแล้วค่อยส่งออกไปทำให้ประหยัดเวลาในการส่งไปเยอะทีเดียว



ตัวอย่าง RTTTL ของเพลง Titanic
จากรูปแสดงตัวอย่างของ RTTTL ของเพลงจากภาพยนต์เรื่อง Titanic ครับ RTTTL แบ่งเป็นสามส่วนหลักคือ
1. ชื่อของ ring tone ใช้อักษรภาษาอังกฤษไม่เกิน 15 ตัวอักษร
2. ค่าคงที่ได้แก่ octave (ระดับเสียง) ระยะเวลา (duration) และความเร็ว (beat)
3. ส่วนที่เป็นทำนองเพลง
สำหรับรายละเอียดท่านสามารถศึกษาต่อได้จากลิงค์ที่ให้ไว้ครับ

กลับมาสู่เรื่องของ Raspberry Pi ครับ ผมจะนำเอา RTTTL มาใช้กับโครงงานนี้ครับ โดยมีขั้นตอนดังนี้

1. ติดตั้ง RTTTL Parser

เนื่องจาก RTTTL นั้นเป็นภาษาดังนั้นเราต้องมีตัวแปรภาษาหรือ parser เพื่อเปลี่ยน RTTTL ให้อยู่ในรูปแบบที่เราเข้าใจและทำงานต่อไปได้ โชคดีที่ทาง Nokia ได้มีการสร้าง parser ในภาษา Python ไว้และมีคนที่นำมาบำรุงรักษาต่อไว้ สามารถทำการติดตั้งได้สองทางคือ

ทางเลือกที่ 1


python2

$ sudo pip install rtttl

 python3
$ sudo pip3  install rtttl


ทางเลือกที่ 2

ติดตั้งจาก source code
$ git clone https://github.com/asdwsda/rtttl.git
$ cd rtttl
$ python setup.py install  หรือ
$ python3 setup.py install


2. หา RTTTL ringtone

เราสามารถหา Free ringtone ที่อยู่ในรูปแบบของ RTTTL ได้มากมายจากการเว็บไซต์ต่าง ๆ  เช่น

  • http://www.convertyourtone.com/ringtones.html
  • http://www.vex.net/~lawrence/ringtones.html
  • http://mines.lumpylumpy.com/Electronics/Computers/Software/Cpp/MFC/RingTones.RTTTL

ฯลฯ

3. ประกอบเครื่องเสียง

เนื่องจากผมจะใช้ GPIO กับลำโพงเล็ก ๆ ขนาด 0.5 W หรือจะใช้ Buzzer ก็ได้ ในการทำให้เกิดเสียง ring tone  แล้วนำมาต่อกับ Raspberry Pi ดังภาพ จะเห็นว่ามีการต่อ PNP transistor คั่นระหว่างขา GPIO ที่ 15 กับสาย Ground ของลำโพง ทั้งนี้ก็เพื่อให้เกิด square wave ซึ่งผมว่ามันให้เสียงที่ดีกว่าการต่อสาย GPIO เข้ากับตัวลำโพงหรือ buzzer โดยตรง ครับ



4. เขียน Code กัน


RPi.GPIO version

#--- (1)
import RPi.GPIO as GPIO   
import time 
from rtttl import parse_rtttl

#--- (2)  
buzzer_pin = 22                  
GPIO.setmode(GPIO.BCM)
GPIO.setup(buzzer_pin, GPIO.OUT) # --- (3)


def play_tone(freq,duration):
 duration = duration/1000 #--- (8)
 if freq == 0 : # --- (9)
  time.sleep(duration)
  return
 period = 1.0 / freq   #--- (10) 
 delay = period / 2.0  #--- (11)  
 cycles = int(duration * freq) # --- (12)
 
 for i in range(cycles): # --- (13)
  GPIO.output(buzzer_pin, True)  # --- (14)
  time.sleep(delay)    
  GPIO.output(buzzer_pin, False)  # --- (15) 
  time.sleep(delay)    


# --- (4)
rttl_str ='MImp:d=32,o=5,b=125:d6,d#6,d6,d#6,d6,d#6,d6,\
d#6,d6,d#6,d6,d#6,d6,d#6,d6,d#6,d6,d#6,d6,d#6,e6,f#6,d#6,\
8g.6,8g.,8g.,8a#,8c6,8g.,8g.,8f,8f#,8g.,8g.,8a#,8c6,8g.,\
8g.,8f,8f#,16a#6,16g6,2d6,16a#6,16g6,2c#6,16a#6,16g6,\
2c6,16a#,8c.6,4p,16a#,16g,2f#6,16a#,16g,2f6,16a#,16g,2e6,16d#6,8d.6'

parsed_rtttl = parse_rtttl(rttl_str)  # --- (5)

notes = parsed_rtttl['notes'] # --- (6)
for n in notes :
 play_tone(n['frequency'],n['duration']) # --- (7)
 
  
GPIO.cleanup() # --- (16)


TH_GPIO version

#--- (1)
import time 
from rtttl import parse_rtttl
form th_gpio import TH_GPIO
#--- (2)  
buzzer_pin = 15                  
TH_GPIO().enable_pin(buzzer_pin,'out')# --- (3)


def play_tone(freq,duration):
 duration = duration/1000 #--- (8)
 if freq == 0 : # --- (9)
  time.sleep(duration)
  return
 period = 1.0 / freq   #--- (10) 
 delay = period / 2.0  #--- (11)  
 cycles = int(duration * freq) # --- (12)
 
 for i in range(cycles): # --- (13)
  TH_GPIO().send(buzzer_pin, True)  # --- (14)
  time.sleep(delay)    
  TH_GPIO().send(buzzer_pin, False)  # --- (15) 
  time.sleep(delay)    


# --- (4)
rttl_str ='MImp:d=32,o=5,b=125:d6,d#6,d6,d#6,d6,d#6,d6,\
d#6,d6,d#6,d6,d#6,d6,d#6,d6,d#6,d6,d#6,d6,d#6,e6,f#6,d#6,\
8g.6,8g.,8g.,8a#,8c6,8g.,8g.,8f,8f#,8g.,8g.,8a#,8c6,8g.,\
8g.,8f,8f#,16a#6,16g6,2d6,16a#6,16g6,2c#6,16a#6,16g6,\
2c6,16a#,8c.6,4p,16a#,16g,2f#6,16a#,16g,2f6,16a#,16g,2e6,16d#6,8d.6'

parsed_rtttl = parse_rtttl(rttl_str)  # --- (5)

notes = parsed_rtttl['notes'] # --- (6)
for n in notes :
 play_tone(n['frequency'],n['duration']) # --- (7)
 
  
TH_GPIO().disable_pin(buzzer_pin) # --- (16)

คำอธิบาย

ส่วนที่ (1) เป็นการนำเข้า libraries ที่ต้องการคือ RPi.GPIO , time และ rtttl ซึ่งจะใช้แต่ parse_rtttl ครับ

ส่วนที่ (2) เป็นการกำหนดคุณสมบัติทั่วไปสำหรับการทำงานกับ GPIO

ส่วนที่ (3) กำหนด mode ของ GPIO Pin เป็น output

ส่วนที่ (4) ระบุ RTTTL ของ ring tone ที่ต้องการ ในตัวอย่างเป็นเพลงประกอบภาพยนต์เรื่องหนึ่ง ครับ

ส่วนที่ (5) ทำการแยกหรือแปลความจาก RTTTL ตรงนี้ต้องขยายความกันหน่อย จากรูปแบบของ RTTTL จะเห็นว่าเป็นข้อมูลแบบตัวอักษร แต่การที่เราจะทำให้ Raspberry Pi สร้างเสียงขึ้นมาได้นั้นเราต้องการข้อมูลที่บอกถึงความถี่เสียง (pitch) และระยะเวลาของการทำเสียงนั้น ๆ (duration)  ซึ่งตรงนี้เองที่ parse_rtttl() จาก rtttl library จะรับทำหน้าที่ตรงนี้ เราเพียงแต่ส่งค่า RTTTL ที่เป็นแบบตัวอักษรเข้าไปเท่านั้น ผลที่ได้คือ dictionary ที่บรรจุข้อมูลในรูปแบบ

{'notes' : [{'frequency': xxxxxx,'duration':xxxxx},...],'title':xxxxxx}

ซึ่งข้อมูลที่เราต้องการนำไปใช้ในการทำเสียงคือข้อมูลในส่วนของ 'notes' เท่านั้น ซึ่งก็คือ array ที่บรรจุข้อมูล frequency และ duration ของ note แต่ละตัวนั่นเอง ครับ ข้อมูลแบบนี้จะนำไปใช้ต่อได้ละ

ส่วนที่ (6) จากส่วนที่ (5)  ทำการพักข้อมูลไว้ในตัวแปร notes (ข้ามไปก็ได้ แต่ก็ทำเพื่อให้ code อ่านง่ายขึ้น)

ส่วนที่ (7) ตอนนี้ทำการส่ง frequency และ duration ของ note แต่ละตัวไปยัง play_tone เพื่อทำการสร้างเสียงต่อไป

ส่วนที่ (8) ตรงนี้ผมนำเอา duration ไปหารด้วย 1000 เพราะว่าค่าของ duration ที่ได้มาในตอนแรกจะมีหน่วยเป็น millisecond ครับ แต่ python library ที่เรานำมาใช้นั้นมองเวลาในหน่วย second ดังนั้นต้องแปลงหน่วย ครับ

ส่วนที่ (9) เป็นไปได้ว่า note บางตัวจะไม่มีการทำเสียง เช่น ตัวหยุด ซึ่งความถี่ก็จะเป็นศูนย์ ดังนั้นก็ไม่ต้องทำอะไร แค่หยุดเวลาไว้ก็พอ

ส่วนที่ (10)  period  = 1.0 / freq เป็นที่ทราบกันว่า frequency คือจำนวนคลื่นต่อหนึ่งหน่วยเวลา ดังนั้นส่วนกลับของ frequency ก็คือ คาบ (period) ของคลื่น 1 ลูกนั่นเอง



ภาพจากเว็บ http://www.mediacollege.com/audio/01/sound-waves.html

ส่วนที่ (11)  delay = period / 2.0

ภาพจากเว็บ http://www.mediacollege.com/audio/01/sound-waves.html
จากรูปแสดงให้เห็นการปรับเปลี่ยนค่าแรงดันไฟฟ้าเพื่อให้เกิดคลื่นเสียง จะเห็นว่าคลื่นเสียงแต่ละลูกจะมีการเปลี่ยนแปลงสองระดับคือ 1.0 และ  -1.0 หรือมองในมุมมองของการเขียน code แล้วก็คือค่า True และ False ตามลำดับ ด้วยเหตุผลนี้เองทำให้เราต้องหาร period  (เวลาของคลื่น 1 ลูก) ด้วยสอง เพราะครึ่งแรกจะต้องแทนค่าของเวลาที่แรงดันที่เป็น 1.0 หรือ True อีกครึ่งที่เหลือคือเวลาที่ค่าแรงดัน -1.0 หรือ False ครับ

ส่วนที่ (12)  circle  = duration * freq
จากสองส่วนก่อนหน้าทำให้ทราบแล้วว่า การสร้างเสียงผ่าน GPIO นั้นก็คือการเปลี่ยนแปลงค่าแรงดันไฟฟ้าจาก 1 > 0 หรือ 0 > 1 ในช่วงเวลาหนึ่งนั่นเอง แต่ในทางการทำเป็นเสียงเพลงแล้ว musical note 1 ตัวไม่ได้เท่ากับคลื่นเสียง 1 ลูกเสมอไป อาจเทียบเท่ากับคลื่นมากกว่า 1 ลูกก็ได้ ดังนั้น ช่วงเวลาในการสร้างคลื่นเสียงสำหรับ musical note 1 ตัวจึงเท่ากับผลคูณของ frequency ของ musical note ตัวนั้นกับ duration ในการเล่นของมัน ครับ

ส่วนที่ (13) จึงเป็นการวนลูป (looping) เพืื่อสร้างเสียงให้กับ musical note 1 ตัว ตามหลักการที่อธิบายมาแล้วจาก (10) - (12) ครับ

ส่วนที่ (14) - ส่วนที่ (15) คือการปรับเปลี่ยนแรงดันไฟฟ้าให้กับ GPIO

ส่วนที่ (16) การล้างค่าตัวแปรต่าง ให้ักับ GPIO

ดูผลงาน




Previous
Next Post »