MIDI SoftSynth บนลินุกซ์ด้วย ALSA VirMIDI + TiMidity

ปัญหานึงของซาวด์การ์ดหลายๆ รุ่นคือไม่มีมิดี้ที่เป็นฮาร์ดแวร์ หรือ FM Synth ก็เลยไม่สามารถเล่นมิดี้บนลินุกซ์ได้ (ถ้าเป็นวินโดวส์ ไดรเวอร์จะจำลองมิดิ้ให้ เป็นซอฟต์ซินธ์ด้วย (SoftSynth/Software Wave Table Emulation) แต่นับว่าโชคดีที่ลินุกซ์ทะเลใช้ระบบเสียงของ ALSA (Advanced Linux Sound Architecture) ก็เลยมีทางที่จะเซ็ตซอฟต์ซินธ์บลินุกซ์ด้วยเหมือนกันโดยใช้ไดรเวอร์ Virtual MIDI และซอฟต์แวร์ชื่อ TiMidity++ … ขั้นตอนมีดังนี้ครับ

เริ่มกันที่ ALSA

คอมไฟล์ alsa-driver โดยระบุให้สร้างไดรเวอร์ virmidi ด้วย ถ้าติดตั้งลินุกซ์ทะเลปกติ จะมีไดรเวอร์นี้อยู่แล้ว แต่สำหรับคนที่ recompile alsa-driver ใหม่ถ้าระบุออพชั่น –with-cards ให้ใส่ virmidi เข้าไปด้วยประมาณนี้

./configure --with-sequencer=yes --with-oss=yes --with-cards=virmidi,intel8x0

จากนั้นก็ make และ make install ตามปกติ หลังจากติดตั้ง alsa-driver แล้ว ทีนี้ก็ลอง load kernel module กัน ..

[kitt@peorth kitt]$ modprobe snd-virmidi index=1

ถ้า lsmod ดูควรจะเห็นอะไรประมาณนี้

snd-virmidi             2144   0 (autoclean)
snd-seq-virmidi         5096   0 (autoclean) [snd-virmidi]
snd-seq-midi-event      5672   0 (autoclean) [snd-seq-virmidi]
snd-seq                47408   0 (autoclean) [snd-seq-virmidi snd-seq-midi-event]
snd-intel8x0           24228   1
snd-pcm                83360   0 [snd-intel8x0]
snd-timer              19688   0 [snd-seq snd-pcm]
snd-ac97-codec         44640   0 [snd-intel8x0]
snd-page-alloc          8552   0 [snd-intel8x0 snd-pcm]
snd-mpu401-uart         5184   0 [snd-intel8x0]
snd-rawmidi            18752   0 [snd-seq-virmidi snd-mpu401-uart]
snd-seq-device          6364   0 [snd-seq snd-rawmidi]
snd                    43332   0 [snd-mixer-oss snd-virmidi snd-seq-virmidi
snd-seq-midi-event snd-seq snd-intel8x0 snd-pcm snd-timer snd-ac97-codec
snd-mpu401-uart snd-rawmidi snd-seq-device]

ลองสั่ง cat /proc/asound/cards

[kitt@peorth kitt]$ cat /proc/asound/cards
0 [82801CAICH3    ]: ICH - Intel 82801CA-ICH3
                     Intel 82801CA-ICH3 at 0x9800, irq 10
1 [VirMIDI        ]: VirMIDI - VirMIDI
                     Virtual MIDI Card 1

จะเห็นว่ามีซาวด์การ์ดสองใบ หนึ่งในนั้นเป็นซาวด์การ์ดจริง (ในตัวอย่างนี้คือ 82801CA ICH3 ของชิพเซ็ต i830) อีกตัวเป็น VirMIDI เป็นการ์ดที่จำลองขึ้นมาโดย module snd-virmidi ..

TiMidity

ที่จริงลินุกซ์ทะเลให้ TiMidity++ มาด้วยนะครับ แต่ว่าไม่ได้คอมไพล์ให้ใช้กับ ALSA ได้ .. ก็ต้องดาวน์โหลดเวอร์ชันที่คอมไพล์ ALSA และ ALSA Sequencer Client ด้วย .. ไม่ต้องไปหาไกล ผมทำให้แล้วล่ะ คิดว่าคงอยู่ใน TLE Update แล้ว จะใช้ Synaptic หรือจะสั่ง apt-get install หรือ apt-get update/upgrade เอาก็ได้ หรือดาวน์โหลด rpm ก็ได้ที่

ftp://ftp.kitty.in.th/pub/rpms/timidity++-2.11.3-4_1kit.i386.rpm

เชื่อม Virtual MIDI กับ TiMidity

Virtual MIDI ไม่ได้เล่นมิดี้ได้ด้วยตัวเองครับ มันแค่จำลองเป็นมิดี้อินเทอร์เฟซเท่านั้น จะทำให้มันเล่นมิดี้ มีเสียงได้ต้องทำให้ Virtual MIDI ส่งข้อมูลมิดี้ไปยัง TiMidity++ ให้ได้เสียก่อน .. อืมม จะอธิบายหลักการตรงนี้ก็เป็นเรื่องยาว .. ขอตัดบทเลยก็แล้วกัน ก่อนอื่น TiMidity++ เป็นโหมด ALSA Sequencer Client โดยระบุออพชัน -iA เข้าไป

[kitt@peorth kitt]$ timidity -iA -Os &
[1] 1106
Requested buffer size 32768, fragment size 8192
ALSA pcm 'default' set buffer size 32768, period size 8192 bytes
TiMidity starting in ALSA server mode
can't set sched_setscheduler - using normal priority
Opening sequencer port: 128:0 128:1

จากนั้นหาหมายเลขพอร์ตของ Virtual MIDI โดยดูจากไฟล์ /proc/asound/clients หรือ /proc/asound/seq/clients

[kitt@peorth kitt]$ cat /proc/asound/seq/clients
Client info
  cur  clients : 6
  peak clients : 6
  max  clients : 192

Client   0 : "System" [Kernel]
  Port   0 : "Timer" (Rwe-)
  Port   1 : "Announce" (R-e-)
Client  72 : "Virtual Raw MIDI 1-0" [Kernel]
  Port   0 : "VirMIDI 1-0" (RWeX)
Client  73 : "Virtual Raw MIDI 1-1" [Kernel]
  Port   0 : "VirMIDI 1-1" (RWeX)
Client  74 : "Virtual Raw MIDI 1-2" [Kernel]
  Port   0 : "VirMIDI 1-2" (RWeX)
Client  75 : "Virtual Raw MIDI 1-3" [Kernel]
  Port   0 : "VirMIDI 1-3" (RWeX)
Client 128 : "Client-128" [User]
  Port   0 : "TiMidity port 0" (-We-)
  Port   1 : "TiMidity port 1" (-We-)
  Output pool :
    Pool size          : 500
    Cells in use       : 0
    Peak cells in use  : 0
    Alloc success      : 0
    Alloc failures     : 0
  Input pool :
    Pool size          : 1000
    Cells in use       : 0
    Peak cells in use  : 0
    Alloc success      : 0
    Alloc failures     : 0

จะเห็นว่ามี Virtual RAW MIDI อยู่ 4 ไคลเอนด์ หมายเลข 72, 73, 74 และ 75 ตามลำดับ ทุกตัวมีพอร์ต 0 … ส่วน TiMidity++ อยู่ที่ 128 มีสองพอร์ตคือ 0 และ 1 .. เมื่อได้หมายเลขพอร์ตแล้วก็สั่งเชื่อมพอร์ตได้แล้ว ที่ต้องทำก็คือเชื่อมพอร์ต 0 ของ Virtual RAW MIDI ตัวแรก เข้าไปที่พอร์ต 0 ของ TiMidity++ โดยคำสั่ง aconnect

[kitt@peorth kitt]$ aconnect 72:0 128:0

ดู /proc/asound/seq/clients อีกครั้ง

[kitt@peorth kitt]$ cat /proc/asound/seq/clients
Client info
  cur  clients : 6
  peak clients : 7
  max  clients : 192

Client   0 : "System" [Kernel]
  Port   0 : "Timer" (Rwe-)
  Port   1 : "Announce" (R-e-)
Client  72 : "Virtual Raw MIDI 1-0" [Kernel]
  Port   0 : "VirMIDI 1-0" (RWeX)
    Connecting To: 128:0
Client  73 : "Virtual Raw MIDI 1-1" [Kernel]
  Port   0 : "VirMIDI 1-1" (RWeX)
Client  74 : "Virtual Raw MIDI 1-2" [Kernel]
  Port   0 : "VirMIDI 1-2" (RWeX)
Client  75 : "Virtual Raw MIDI 1-3" [Kernel]
  Port   0 : "VirMIDI 1-3" (RWeX)
Client 128 : "Client-128" [User]
  Port   0 : "TiMidity port 0" (-We-)
    Connected From: 72:0
  Port   1 : "TiMidity port 1" (-We-)
  Output pool :
    Pool size          : 500
    Cells in use       : 0
    Peak cells in use  : 0
    Alloc success      : 0
    Alloc failures     : 0
  Input pool :
    Pool size          : 1000
    Cells in use       : 0
    Peak cells in use  : 1
    Alloc success      : 1
    Alloc failures     : 0

จะเห็นว่า Client 72 มีข้อความ Connected To: 128:0 และที่ TiMidity++ มีข้อความ Connected From: 72:0

แสดงว่าเชื่อมต่อเป็นที่เรียบร้อยแล้ว ถ้าสั่ง playmidi หรือใช้โปรแกรมอะไรก็ตามที่ใช้งานมิดี้ ข้อมูลของมิดี้จะส่งผ่าน external MIDI ซึ่งจำลองขึ้นมาโดยไดรเวอร์ Virtual MIDI ไปยัง TiMidity++ แล้วเราก็จะได้ยินเสียงมิดี้กันล่ะ :)

คอนฟิกระบบ

ถ้าต้องการคอนฟิกให้ลินุกซ์เรียก Virtual MIDI อัตโนมัติทุกครั้งที่บูตก็สามารถทำได้โดยแก้ /etc/modules.conf ประมาณนี้

alias snd-card-0 snd-intel8x0
alias snd-card-1 snd-virmidi
alias sound-slot-0 snd-card-0
alias sound-slot-1 snd-card-1
alias char-major-116 snd
alias char-major-14 soundcore
options snd major=116 cards_limit=2
options snd-intel8x0 index=0
options snd-virmidi index=1
alias sound-service-0-0 snd-mixer-oss
alias sound-service-0-1 snd-seq-oss
alias sound-service-0-3 snd-pcm-oss
alias sound-service-0-8 snd-seq-oss
alias sound-service-0-12 snd-pcm-oss

ที่ต้องเพิ่มเข้าไปใน /etc/modules.conf ก็คือ บรรทัด

alias snd-card-1 snd-virmidi
alias sound-slot-1 snd-card-1
options snd-virmidi index=1

และต้องเปลี่ยน cards_limit เป็น 2

ทีนี้ทุกครั้งที่บูต snd-virmidi module ก็จะได้รับการติดตั้งอัตโนมัติ ส่วนการเชื่อม Virtual MIDI กับ TiMidity++ ทำเป็นครั้งๆ ไปเมื่อต้องการใช้ดีกว่า เพราะยังต้องรัน timidity -iA -Os ก่อนถึงจะสั่ง aconnect ได้ .. จะให้มันรันตอนบูตเลยดูจะเป็นการเปลืองทรัพยากรระบบเปล่าๆ

อื่นๆ

ยังมีเทคนิคอีกเล็กน้อยสำหรับ TiMidity++ เพื่อให้การทำงานกับมิดี้ตอบสนองได้ดีขึ้นครับ … ปกติ TiMidity ใช้บัฟเฟอร์ขนาดใหญ่เพื่อให้เพลย์ได้ราบรื่น แต่ก็ทำให้มี latency เยอะซึ่งไม่เหมาะกับโปรแกรมที่ต้องการทำงานแบบเรียลไทม์ เช่น โปรแกรมซีเควนเซอร์ อย่าง Rosegarden หรือ Noteedit ทางแก้คือกำหนดบัฟเฟอร์ให้เล็กลงด้วยออพชัน -B ค่าที่เหมาะๆ มีคนแนะมาว่าใช้ -B2,8 ส่วนตัวผมใช้ -B8,8 กำลังพอดี

ถ้า TiMidity++ กินซีพียูเยอะ และต้องการลดมันลง สามารถทำได้โดยยกเลิกเอฟเฟกต์ต่างๆ เช่น reverb และ/หรือ chorus โดยกำหนดออพชัน -EFreverb=0 และ/หรือ -EFchorus=0 ตามลำดับ

ถ้ายังไม่พอใจอีก ก็ต้องลองรัน TiMidity++ ด้วย root หรือตั้ง suid root วิธีนี้ทำให้ TiMidity++ สามารถตั้ง scheduling เป็น FIFO ด้วย priority สูงสุดเท่าที่จะทำได้ (ซึ่งมีเฉพาะ root ที่มีสิทธิตั้งค่าแบบนั้น) วิธีนี้ทำให้การตอบสนองดีขึ้นโดยเฉพาะกับโปรแกรมที่ทำงานแบบเรียลไทม์

.. วันนี้ยาวแฮะ

ใช้ภาษาญี่ปุ่นบนลินุกซ์

พยายามกับภาษาญี่ปุ่นบนลินุกซ์มานานแล้ว วันนี้ก็เห็นผลเสียทีครับ เรื่องของเรื่องก็คือลินุกซ์ทะเล 5.0 ไม่มีภาษาญี่ปุ่นให้เลือก ตอนติดตั้ง .. glibc ก็เลยไม่สนับสนุน locale ญี่ปุ่นเลย หลังจากเอาเวลาว่างๆ ไปค้นข้อมูลหลายหน ก็ได้ข้อสรุปมาบ้างแล้ว

อย่างแรกเลยคือเรื่องของ locale ภาษาญี่ปุ่นที่ต้องเพิ่มเข้าไปใน glibc .. ทำได้ไม่ยากเลย แต่กว่าจะรู้วิธีนี้ก็เสียเวลางมหาซะนาน กุญแจอยู่ที่ localedef นั่นเองครับ .. สำหรับภาษาญี่ปุ่นนิยมใช้ ja_JP.eucJP ก็ localedef ได้เป็น

# localedef -v -c -i ja_JP -f EUC-JP /usr/lib/locale/ja_JP.eucJP

ลองสั่ง locale -a ดูถ้ามี ja_JP ก็ใช้ได้แล้ว

อย่างที่สองคือ keyboard input .. ต้องติดตั้งหลายตัวหน่อย

Canna
Canna-libs
FreeWnn
FreeWnn-libs
FreeWnn-common
Wnn6-SDK
kinput2-canna-wnn6
kterm

ไม่ต้องคิดมาก ลงๆ ไปเลย apt-get install kinput2 เอาก็ได้ เดี๋ยวมันลาก dependencies มาลงให้เอง :)

วิธีทดสอบ

ก็ลองกันด้วย kterm นี่ล่ะครับ .. สตาร์ท canna เซิร์ฟเวอร์ก่อน ตามด้วย kinput2 เพื่อเชื่อมกับ canna แล้วค่อยเรียก kterm:

# service canna start
# kinput2 -canna &
# kterm &

ใน kterm กด shift-space จะเข้าสู่โหมด input ภาษาญี่ปุ่น ลองพิมพ์ภาษาญี่ปุ่นด้วยอักษรโรมาจิ .. เอาเป็น konnichiha ก็ได้ จะเห็นว่ามันแสดงเป็นตัวอักษรฮิระงานะ มีเส้นขีดใต้ข้อความ เส้นนี้เป็นตัวบอกว่าข้อความไหนที่ kinput2 กำลังประมวลผลอยู่ ลองเคาะ space ดูมันจะแสดงตัวคันจิขึ้นมาด้วย ถ้าเคาะ space อีกครั้ง kinput2 จะแสดงหน้าต่างให้เลือกตัวคันจิ คะตะคะนะ และฮิระงานะด้วย

ใช้ space หรือปุ่มลูกศรเลือกคำที่ต้องการ แล้วก็เคาะ enter .. หน้าต่างก็จะปิดลง เคาะ enter อีกครั้ง เส้นใต้ข้อความจะหายไป กด shift-space อีกครั้งก็จะออกจากโหมด input ภาษาญี่ปุ่น ..พอใช้ kterm ได้โปรแกรมอื่นๆ ที่ run เป็น text mode บน kterm ก็ควรจะใช้ภาษาญี่ปุ่นได้ด้วย .. ลองดูก็แล้วกัน

บน GNOME2

สำหรับ GNOME2 สามารถเซ็ตให้โปรแกรมใช้ kinput2 ได้โดยใช้ XIM .. อย่างแรกก็ต้องตั้ง XMODIFIERS ให้ input method เป็น kinput2 ก่อน

export XMODIFIERS="@im=kinput2"

ทีนี้ kinput2 จะทำงานอัตโนมัติถ้าโปรแกรมนั้นทำงานด้วย locale ญี่ปุ่น สมมติเป็น gedit บน terminal สั่ง

LANG=ja_JP gedit

จะ start gedit โดยกำหนดให้ LANG ที่ใช้เป็น ja_JP ครับ ทีนี้ใน gedit ให้คลิกขวาเลือก input method เป็น X Input Method ก็จะใช้ภาษาญี่ปุ่นได้เหมือนบน kterm .. อ่อ อย่าลืมเปลี่ยนฟอนต์เป็นภาษาญี่ปุ่นด้วยนะครับ ถ้าไม่มีก็ติดตั้งแพคเกจ ttfonts-ja ได้จากแผ่นติดตั้งลินุกซ์ทะเล หรือ apt-get install เอาก็ได้

เฮ่อ .. ได้ขนาดนี้ก็พอใจแล้ว .. วันนี้เอาไว้เท่านี้ก่อนครับ :)