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 กัน ..

[[email protected] 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

[[email protected] 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 เข้าไป

[[email protected] 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

[[email protected] 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

[[email protected] kitt]$ aconnect 72:0 128:0

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

[[email protected] 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 ที่มีสิทธิตั้งค่าแบบนั้น) วิธีนี้ทำให้การตอบสนองดีขึ้นโดยเฉพาะกับโปรแกรมที่ทำงานแบบเรียลไทม์

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