โดยปกติเราสามารถติดตั้งภาษา java ลงไปได้ง่ายๆ โดยใช้คำสั่งในเทอร์มินัล
sudo apt-get install default-jdk (หรือ  default-jre ในการใช้งานปกติทั่วไป)
ซึ่งเราจะได้ java-6-openjdk มาใช้โดยอัตโนมัติ
 
เมื่อเราต้องการติดตั้ง sun-6-jdk เราจะใช้คำสั่งว่า
sudo apt-get install sun-java6-jdk ( หรือ sun-java6-jre สำหรับการใช้งานปกติทั่วไป)
 
เราอาจพบปัญหาว่าเมื่อติดตั้งแล้ว java ไม่เปลี่ยนจาก openjdk มาเป็น sun มีวิธีแก้ปัญหาง่ายๆดังนี้คือ
ใช้คำสั่งต่อไปนี้เพื่อดูว่า มี java อะไรติดตั้งอยู่บ้าง ด้วยคำสั่ง
sudo update-java-alternatives -l
น่าจะพบดังตัวอย่าง
java-6-openjdk 1061 /usr/lib/jvm/java-6-openjdk
java-6-sun 63 /usr/lib/jvm/java-6-sun
 
ใช้คำสั่งต่อไปนี้เพื่อเปลี่ยนจาก openjdk ไปเป็น sun
sudo update-java-alternatives -s java-6-sun
และเปลี่ยนกลับจาก sun ไปเป็น openjdk ด้วคำสั่ง
sudo update-java-alternatives -s java-6-openjdk
เมื่อเปลี่ยนไปแล้วสามารถตรวจสอบการเปลี่ยนแปลงได้ด้วยคำสั่ง
java -version
ตัวอย่างนี้เขียนขึ้นมาด้วยภาษา Groovy ผมใช้เป็นกรณีศึกษาในการศึกษาภาษา Groovy และทดลองอ่านไฟล์ Nick Karaoke หรือ NCN เหมาะสำหรับผู้สนใจจะนำไปศึกษาค้นคว้าต่อไป
 
 
import javax.sound.midi.*

def txt = new File('Lyrics/0/','00001.LYR').readBytes()
def dat = new File('Cursor/0/','00001.CUR').readBytes()
def mid = new File('Song/0/','00001.MID')
def sequence = MidiSystem.getSequence(mid) // get sequence from midi file
def sequencer = MidiSystem.getSequencer() // get midi player from system

def s = new String(txt, 'TIS-620') // read text in thai format
def index = s.indexOf('\r\n\r\n') + 4 // check position that is the 4th line
def ts = s.substring(index) // cut txt head
def maxx = ts.size() <= dat.size() /2 ? ts.size() : dat.size() /2 // use less size
def cur = [:]
def cc = 0
def k // cursor position
for(int i = 0; i+1 < maxx; i++) {
    def b1 = dat[i*2]
    def b2 = dat[i*2+1]
    if(b1 == 0xff || b2 == 0xff) continue
    k = (long) ((b1 + (b2 << 8)) * sequence.resolution / 24 ) // cursor position formular
    while (cur.containsKey(k)) k ++ // select new key because sometimes cursor position is duplicate
    def ch = ts.charAt(cc)
    if (ch == '\r') {
        cc++
        maxx --
    }
    cur[k] = ts.charAt(cc) // map cursorposition and lyric charactor
    cc++
}
for(int i = maxx; i < ts.size(); i++ ) { // incase that is some lyrics has no cursor
    while(cur.containsKey(k)) k++
    if(cc >= ts.size()) break
    def ch = ts.charAt(cc)
    if (ch == '\r') {
        cc++
        maxx --
    }
    cur[k] = ts.charAt(cc)
    cc++
}

sequencer.open()
sequencer.sequence = sequence

def soundbank = null
if(args.size() > 0) {
    print 'Loading Sound Bank ' + args[0] + ' '
    File file = new File(args[0])
    soundbank = MidiSystem.getSoundbank(file) // loda soundfont
    println 'done.'           
}

def receiver
def transmitter = sequencer.transmitter // get transmitter
def synthesizer
if (! (sequencer in Synthesizer)) { // set default synthesizer
    println 'using default synthesizer'
    synthesizer = MidiSystem.synthesizer
    synthesizer.open()
    receiver = synthesizer.receiver
    sequencer.transmitter.receiver = receiver
}else { // use synthesizer from the song
    println 'using song synthesizer'
    synthesizer = sequencer
}
if (soundbank != null) { // set soundfont
    synthesizer.open()
    println "soundbank supported: " + synthesizer.isSoundbankSupported(soundbank)
    print 'loading instruments...'
    synthesizer.loadAllInstruments(soundbank)
    println 'done.'
}

def end = false
sequencer.addMetaEventListener([ // set listener to trick system to end
    meta: { e ->
        if(e.getType() == 47) {
            sequencer.close()
            end = true
        }
    }
] as MetaEventListener)

sequencer.start() // start playing

println 'tick per beat ' + sequence.resolution
println '\033[34m' + s.substring(0, index) // print song header text

def lyr = s.substring(index).split('\n')
def step = sequence.resolution / 8
def ref = 0
def count = 0
def ncount = 1
print '\033[30m' + lyr[0] + '\r' // print song first line
cur.each {
    def current = it.key
    if(current > ref) {
        ref = current
        count = 0
    } else {
        count++
        current = ref + step * count
    }
    // check if sequence is in position
    while(sequencer.tickPosition < current) { // if tick position is in the position waiting
        Thread.sleep(200)
        if(end) break
    }
    def value = it.value
    print '\033[31m' + value // print song text when meet the tick position
    if(value == '\n' && ncount < lyr.size()) {
        print '\033[30m' + lyr[ncount]
        ncount++
    }
}

println '\033[30m'

กรณีศึกษาไฟล์ Karaoke แบบ NCN

posted on 19 Dec 2010 03:28 by asuwannarat in JavaAndMe
จากรายละเอียดที่เผยแพร่เกี่ยวกับไฟล์ Nick Karaoke หรือ NCN เป็นไฟล์คาราโอเกะที่มีการแบ่งไฟล์ออกเป็นไฟล์ย่อย 3 ไฟล์คือ .mid, .cur, และ .lyr ซึ่งแต่ละไฟล์จะมีหน้าที่ดังต่อไปนี้
1. MIDI หรือ .mid เป็นไฟล์ที่ทำหน้าที่เก็บข้อมูลเพลงชนิด midi
2. Lyrics หรือ .lyr เป็นไฟล์ที่ทำหน้าที่เก็บเนื้อเพลง จากที่พบจะเป็น charactor ขนาด 1 byte windows format กล่าวคือมี carriage return และ new line อยู่ด้วย
3 Cursor หรือ .cur เป็นไฟล์ที่ทาง Nick Karaoke ออกแบบมา เป็นไฟล์ขนาด 2 bytes ซึ่งใช้สำหรับบอกเวลาในการแสดงตัวอักษรแต่ละตัวใน lyrics โดยมีสูตรคำนวนพอจะเขียนเป็นภาษา java ง่ายๆได้ว่า
 
InputStream min = new FileInputStream(midFile);
Sequence seq = MidiSystem.getSequence(min); // อ่านข้อมูล sequence จาก midi ไฟล์
InputStream cin = new FileInputStream(curFile);
InputStream lin = new FileInputStream(lyrFile);
 
// ตรวจสอบขนาดของไฟล์ทั้งคู่ว่ามีขนาดเท่ากันหรือไม่ ใช้ตัวที่มีค่าน้อยกว่า (size ของ lyr จะเล็กกว่า cur ประมาณเท่าตัว) ก่อนเช็คต้องตัดหัวของข้อมูล lyr ออกไปก่อน 4 บรรทัด (เป็นข้อมูลส่วนที่ไม่ต้องใช้ในการจับคู่กับ midi)
long lyrSize = lin.available();
... // สมมติว่า lyrSize ผ่านกระบวนการ ตัดข้อมูลออกไปแล้ว 4 บรรทัด
long curSize = cin.available();
long maxAvailable = lyrSize <= curSize / 2 ? lyrSize : curSize / 2; // หาขนาดที่สั้นกว่าในการจับคู่ข้อมูลระหว่าง lyr กับ cur
 
for(int i = 0; i < maxAvailable; i++) {
    byte b1 = cin.readByte();
    byte b2 = cin.readByte();
    Long charCursor = (b1 + (b2 << 8)) * sequence.getResolution() / 24;  // ใช้สูตรนี้แล้วจะได้ตำแหน่งของ Cursor ของตัวอักษรแต่ละตัว
/*
จากตรงนี้ จะได้ว่า ที่ตำแหน่ง i จะมี lyr[i] คู่กับ charCursor อยู่ 1 คู่ ซึ่งตรงนี้เราก็จะทำการ listen midi ว่าเล่นมาถึงตำแหน่งของ charCursor หรือยัง ถ้าถึงแล้วก็ทำการ แสดงผล lyr[i] ทันที
*/
}
 
ศึกษารายละเอียดเพิ่มเติมเรื่อง midi ใน java ได้ที่
http://www.jsresources.org/faq_midi.html