Skip to content

Instantly share code, notes, and snippets.

@hsak
Created January 13, 2009 12:57
Show Gist options
  • Save hsak/46435 to your computer and use it in GitHub Desktop.
Save hsak/46435 to your computer and use it in GitHub Desktop.
FM Chip tube on Scala
<html>
<title>Tiny OPM Applet</title>
<body>
<applet code="osc.oscApplet" archive="osc.jar" width=465 height=465>
</applet>
<h3>使い方</h3>
<p>ctrl + S で再生</p>
<p>ctrl + D でSTOP</p>
</body>
</html>
// MML Editor
// - ctrl+S で play
// - ctrl+D で stop
// - ";" 区切りで複数チャンネル
// - "#[_A-Z][_A-Z0-9]*=..."でマクロ定義.
// - シーケンス内"[_A-Z][_A-Z0-9]*"で展開.
// - 最下段で再生開始ポジション/スピードを指定(フレーム単位).
//------------------------------------------------------------
package osc
import scala.List
import java.util.Random
import java.awt._
import java.awt.event._
import javax.swing._
import javax.swing.undo._
import javax.sound.sampled._
import scala.concurrent.ops._
import scala.collection.mutable._
class oscApplet extends JApplet {
val panel = new MMLEditor
override def init() {
val pane = getContentPane()
pane.add(panel)
}
override def start() {
panel.start()
}
override def stop() {
panel.stop()
}
}
object main extends Application {
val panel = new MMLEditor
val window = new JFrame
window.getContentPane().add(panel)
window.show()
window.pack()
window.setResizable(false)
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
}
class MMLEditor extends JPanel with KeyListener{
/*
#BS=;
@0@o1o2BS; v7@0@i3o4BS;
#BD=$[ar];
#SN=;
#CY=;
@0w32o4BD; @3o0SN; @3o0CY;
#GT=$[6a][3b][15f];
@2@o1o6 GT; @0@i3@o1o4k112GT; p1v5@1@i4o3 GT;
@2@o1o6k112GT; @0@i3@o1o4k224GT; p7v4@1@i4o3k112GT;
#SA=<<a4>>>a4<r16;
#SB=<<a4>>b4r16;
#S1=$SA SB;
#S2=;
#S3=;
p6@1S1; p2@1S2; @1S3;
#MA=;
v8o6k-1MA; v10o5k1MA; v3p1o6r4MA;
*/
private var mml =
"""#BS=r28$s10l4[3e8[22e]er<s2c12>s10brg]s1a36g12f+16s12e8[5e]l2egabl4ers2e20
s10[[3e8[22e]er<s2c12>s10brg]e8[15e]gabf+8gae8eeegab
[[<c8[6c]>b8[6b]a8[6a]|g8ggggab]|a8aaaabr]a8[6a]<d8[14d]>]
erer20[8[erer20|erer20]|er28]erer20
[[[<c8[6c]>b8[6b]a8[6a]|g8ggggab]|a8aaaabr]a8[6a]]<d8[14d]>;
@0@o1o2BS; v7@0@i3o4BS;
#BD=r8l4v14s32cc2<v5e2c2>g2v14crl4c$[rcrr[15ccrr]] [[rcrr[15ccrr]]
[rcrr[15ccrr]] [2ccrr]cl2<v6ggggeeeecccc>l4v14c]
rcr20[6[crcr20|crcr20]|cr28][8crcrrccr]crcrrcccr
[4rcrr[15ccrr]] [2ccrr]cl2<v6ggggeeeecccc>l4v14c;
#SN=r8l4v14s12k8cc2k4s32c2c2c6k8s12l8c$k8[14rc]l4ck4ccrk8ccl8c[7rc]r4c4c[5rc]rl2k4ccc8r4k8c8c4c4r8
l8[[[15rc]r4c4c] [k8[13rc]|r4c2c2c4k4l4crccrccl8r] rc[rcr4c4c]c4k4l2s32[3c1c1ccc]r4l8k8s12]
l16[[2rc]|rc4s24k4c4c8s12k8rc4c12]rc4c12k4s32l2[ccc4]k8s12c4c4c8 l8[6[7rc]r4c4c]r16c4c4c8
[4k8[13rc]|r4c2c2c4k4l4crccrccl8r] rc[rcr4c4c]c4k4l2s32[3c1c1ccc]r4l8k8s12;
#CY=r8l8s4v8c+20$l8[3s6p3v16d12s10p5v6[14d]d4]s6v16d12p5v6[3d]p2v12d12p5d16p3v16d44p4d20
[l4[4p5s6v16d12s24p6v6[29d]]l8p5[4s6v16d16s10v8[14d]][4d]p3s4v12c+32]
s64v3l2[512d]v12s6c+8v15c+2r22
l8p5[8s6v16d16s10v8[14d]][4d]p3s4v12c+32;
@0w32o4BD; @3o0SN; @3o0CY;
#GT=r28$l4[3[3[s2e8s12e]ee]ers2b2<c10>brg]s1a36g12f+16s2e40re20
[l4[3[3[s2e8s12e]ee]ers2b2<c10>brg][[s2e8s12e]ee]es1g16f+12e20s12eer
s1l8[<c24c>b24b<d24d>g24g<c24c>b24b|a56b]a64<d64>]
r256s24l4[192e]s12erer20
s1l8[[<c24c>b24b<d24d>g24g<c24c>b24b|a56b]a64]<d64>;
@2@o1o6 GT; @0@i3@o1o4k112GT; p1v5@1@i4o3 GT;
@2@o1o6k112GT; @0@i3@o1o4k224GT; p7v4@1@i4o3k112GT;
#S1=v5r28$o6s6l4e8[4e|r36drdrer]r28[3dr]e8[er36drdr|er]s1c36>b12a12r<erer32d20
s2[[[e32d32|d24crc20d8r]rd28dr|c12>b<rd]g12f+rd
[[er20erdr20dr|dr20drdr12d16]|d24drd24d8]d64d56dr]
v7drdrv1drdr[8[v6drdrv1dv6c+12|v6drdrv1drdr]|drrrv1drrr]drdr20
[[[er20erdr20dr|dr20drdr12d16]|d24drd24d8]d64]d56dr;
#S2=v6r28$o5s6l4g8[4g|r36f+rf+rgr]r28[3f+r]g8[gr36f+rf+r|gr]s1e36e12dr12erer32f+20
s2[[[g32f+32|f+24f+re20f+8r]rf+28f+r|e12drf+]b12brf+
[[gr20grgr20gr|f+r20f+rgr12fgb8]|g24grf+24f+8]g64f+56f+r]
v8grgrv1grgr[8[v6grgrv1gv6g12|v6grgrv1grgr]|grrrv1grrr]grgr20
[[[gr20grgr20gr|f+r20f+rgr12fgb8]|g24grf+24f+8]g64]f+56f+r;
#S3=v6r28$o5s6l4b8[4b|r36ararbr]r28[3ar]b8[br36arar|br]s1a36g12f+12rbrbr32a20
s2>[[[b32a32|a24grg20a8r]ra28ar|g12ara]<<d12d>ra
[[cr20cr>br20br|ar20arbr12b16<]|a56a8<]a64a56ar]<
v8ererv1erer[8[v6ererv1ev6e12|v6ererv1erer]|errrv1errr]erer20
[[[cr20cr>br20br|ar20arbr12b16<]|a56a8<]a64]a56ar<;
p6@1S1; p2@1S2; @1S3;
#MA=r28$@1s3[l16f+1g19f+d>a12b48l4|f+gargre8r112<]r8<cde36g8el8aea+2b2l4rer32d20
@10[s6l4[>[b8bbbbab<c+2d2r>a24|a8aaaagab8<c>ba8eg]r|<f+16g8f+e24r8]<g16f+12e20>
s3b<d>b<e20re8d16>brgra24gab16 fgb<de24f+gd16cr>b8a52<ef+r
g28f+g8d16grf+12gargrarb16<c+1d3rdr>g16<d+1e7dr>g12<cr>ba64f+64];
#MB=r20@2s1w12o9c64s4w-32o4[4c8]r16s2w6o8c64s4w24o5[5c6]r2s1w-12o4c64s0w-2o5c224
@10s3w0o6l4[8rf+de>gb<d>f+abegaf+de<];
#MB2=r772l4<[4raf+g>b<df+>a<de>ab<d>af+g<]>;
#MC=r16>[s3b<d>b<e20re8d16>brgra24gab16 fgb<de24f+gd16cr>b8a52<ef+r
g28f+g8d16grf+12gargrarb16<c+1d3rdr>g16<d+1e7dr>g12<cr>b|a52>]a64<d64>;
v8o6k-1MAv6MBv8MC; v10o5k1MAv3MB2v10MC; v3p1o6r4MAv4MBv3MC;
""";
private var _module:TinySiOPM = null;
private var _sequencer:Sequencer = null;
private var _textField = new JTextArea()
private var _undo = new UndoManager()
private var _statusField = new JLabel()
private var _position = new JTextField()
private var _speed = new JTextField()
setPreferredSize(new Dimension(465, 465))
_textField.addKeyListener(this)
private var loop = false; private var stopf = true
def stop() {
loop = false
}
def start() {
Thread.currentThread.setPriority(10)
var buf = new Array[Byte](4096*2)
val fmt = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED , 44100, 16, 2, 4, 44100, true)
val line = AudioSystem.getLine(
new DataLine.Info(classOf[SourceDataLine], fmt)).asInstanceOf[SourceDataLine]
line.open(); line.start()
loop = true
spawn {
while (loop) {
var size = line.getBufferSize - line.available
if (size > 4096*5) Thread.sleep((size-4096*5)/44)
line.write(buf, 0, buf.length)
if(stopf){for(i<-0 until 4096*2)buf(i)=0} else _onStream(buf)
}
line.drain()
line.stop()
line.close()
}
}
init()
def init() {
setLayout(null)
val format:Font = new Font("MS Gothic", Font.PLAIN, 12)
_textField.setForeground(new Color(0x00ffff))
_textField.setBackground(new Color(0x000000))
_textField.setFont(format)
_textField.getDocument().addUndoableEditListener(_undo)
val scrollpane1 = new JScrollPane(_textField)
scrollpane1.setSize(465, 449)
add(scrollpane1)
_position.setLocation(60,449)
_position.setSize(12,16)
_position.setBackground(new Color(0x80f0c0))
_position.setFont(format)
_position.setText("0")
add(_position)
_speed.setLocation(180,449)
_speed.setSize(12,16)
_speed.setBackground(new Color(0x80f0c0))
_speed.setFont(format)
_speed.setText("2")
add(_speed);
_statusField.setLocation(0,449)
_statusField.setSize(465,16)
_statusField.setBackground(new Color(0x80f0c0))
_statusField.setFont(format)
_statusField.setText("position: speed:")
add(_statusField)
_module = new TinySiOPM(2048, 1024, _onSoundFrame)
_sequencer = new Sequencer()
_textField.setText(mml)
}
def keyPressed(e:KeyEvent) {
if (e.isControlDown() && e.getKeyCode()=='D') {stopf=true}
if (e.isControlDown() && e.getKeyCode()=='S') {
_module.reset()
_sequencer.mml = _expandMML(_textField.getText())
_sequencer.pos = _position.getText().toInt
_sequencer.speed = _speed.getText().toInt
stopf = false
}
if(e.isControlDown() && e.getKeyCode()=='Z' && _undo.canUndo()) _undo.undo()
if(e.isControlDown() && e.getKeyCode()=='Y' && _undo.canRedo()) _undo.redo()
}
def keyReleased(e:KeyEvent) {}
def keyTyped(e:KeyEvent) {}
private def _onSoundFrame() {
_sequencer.onSoundFrame();
}
private def _onStream(data:Array[Byte]) {
var moduleOut = _module.render()
var j = 0
for (i <- 0 until 4096) {
var dt = (moduleOut(i)*4).asInstanceOf[Int]
dt = (if(dt > 0x7fff) 0x7fff else (if(dt < -0x8000) -0x8000 else dt))
// data(i) = (if(dt > 127) 127 else if(dt < -127) -127 else dt).asInstanceOf[Byte]
// data(i) = (dt).asInstanceOf[Byte]
data(j) = (dt >> 8).asInstanceOf[Byte]; j+=1
data(j) = (dt).asInstanceOf[Byte]; j+=1
}
}
private def _expandMML(mml:String) : Stack[String] = {
val sp = mml.replaceAll("""\s+""", "").split(";")
var list= new Stack[String]
var macro = new HashMap[String, String]
val defMacro = """^#([_A-Z][_A-Z0-9]*)=?(.*)""".r
for (seq <- sp) {
seq match {
case defMacro(r1,r2) => macro + (r1 -> r2)
case _ =>
val rep = """([^_A-Z]*)([_A-Z][_A-Z0-9]*)(.*)$""".r
def rt(x:String):String = {
x match {
case rep(x,m,xs) => rt(x + (if (!macro.contains(m)) "" else macro(m)) + xs)
case _ => x
}
}
list.push(rt(seq))
}
}
list
}
}
// MML Sequencer
// http://wonderfl.kayac.com/user/keim_at_Si
//--------------------------------------------------
class Sequencer {
private var _tracks=new Stack[Track]
private var _count=Track.speed+1
def onSoundFrame() : Boolean = {
_count += 1
if (_count == Track.speed) { for(tr <- _tracks) tr.execute(); _count = 0; true } else false
}
def speed = 0;def speed_=(spd:Int) { Track.speed = spd; if (_count >= spd) _count=0 }
def drSpeed = 0;def drSpeed_=(spd:Int) { if (0 <= spd && spd <= 2) Track.drs = spd }
def pos = 0;def pos_=(p:Int) { for (i<- 0 until p; tr <- _tracks) tr.execute() }
def mml = 0;def mml_=(list:Stack[String]) {
_tracks = new Stack;_count = 0
if (list!=null) { for (seq <- list) _tracks.push(new Track(seq)) }
}
}
object Track { var speed=3; var drs=2 }
class Track( seq:String) {
class TrackFrame(var p:List[TD],var c:Int,var j:List[TD])
case class TD(op:String,add:Int,n:Int)
val nt=Array(9,11,0,2,4,5,7); var oct=5;var len=4;var tl = 256;var dt, cnt=0
var sgn:List[TD]=null; var s=new Stack[TrackFrame]
var osc = Osc.alloc().reset().activate(false)
def compile():List[TD] = {
val rx="""(@i|@o|[kloprsvw<>\[|\]$@])([#+])?(-?\d+)?(.*$)""".r
val rx2="""([a-g])([#+])?(-?\d+)?(.*$)""".r
def comp(x:String,l:List[TD]):List[TD] = {
x match {
case rx2(op,a,n,xs) => comp(xs,TD(op, (nt(op.charAt(0)-'a')+{if(a!=null)1 else 0})<<4, if(n!=null)n.toInt else 0) :: l)
case rx("v",_,n,xs) => comp(xs,TD("v", 0, Osc.log((if(n!=null)n.toInt else 0)*0.0625)) :: l)
case rx("p",_,n,xs) => comp(xs,TD("p", 0, if(n!=null){(n.toInt<<4)-64} else 0) :: l)
case rx("[",_,n,xs) => comp(xs,TD("[", 0, if(n!=null)n.toInt else 2)::l)
case rx(op,a,n,xs) => comp(xs,TD(op, 0, if(n!=null)n.toInt else 0) :: l)
case _ => l
}
}
comp(seq,List()).reverse
}
var seqList = compile(); var src = seqList
def execute() {
def exec(x:List[TD], i:Int):List[TD] = {
if(i==0) x else x match {
case TD("r",_,n)::xs=> cnt = if(n!=0) n else len; xs
case TD("k",_,n)::xs=> dt = n;exec(xs,i-1)
case TD("l",_,n)::xs=> len = n;exec(xs,i-1)
case TD("o",_,n)::xs=> oct = n;exec(xs,i-1)
case TD("v",_,n)::xs=> tl = n;exec(xs,i-1)
case TD("<",_,_)::xs=> oct+=1;exec(xs,i-1)
case TD(">",_,_)::xs=> oct-=1;exec(xs,i-1)
case TD("@",_,n)::xs => osc.ws = n;exec(xs,i-1)
case TD("s",_,n)::xs => osc.dr = (n<<Track.drs) & ~1;exec(xs,i-1)
case TD("w",_,n)::xs => osc.sw = -(n>>(2-Track.drs));exec(xs,i-1)
case TD("p",_,n)::xs => osc.pan = n;exec(xs,i-1)
case TD("@i",_,n)::xs=> osc.mod = n;exec(xs,i-1)
case TD("@o",_,n)::xs=> osc.out = n;exec(xs,i-1)
case TD("$",_,_)::xs=> sgn = xs;exec(xs,i-1)
case TD("[",_,n)::xs=> s.push(new TrackFrame(xs,n,src));exec(xs,i-1)
case TD("|",_,_)::xs=> exec(if (s.top.c == 1) s.pop().j else xs,i-1)
case TD("]",_,_)::xs=> s.top.j=xs; s.top.c-=1;exec(if (s.top.c == 0) {s.pop();xs}else s.top.p, i-1)
case TD(op,a,n)::xs => cnt = if(n!=0) n else len; osc.len = cnt * Track.speed; osc.pt = oct*12*16+ a + dt; osc.tl = tl;xs
case _ => if (sgn!=null) exec(sgn,i-1) else { cnt = Integer.MAX_VALUE; List() }
}
}
cnt -= 1
if (cnt <= 0) seqList = exec(seqList, 256)
}
}
class TinySiOPM (_bufferSize:Int, _callbackFrams:Int, _onSoundFrame:()=>Unit) {
private var _output = new Array[Double](_bufferSize*2) // allocate stereo out
private var _zero = new Array[Int](_bufferSize) // allocate zero buffer
private var _pipe = new Array[Int](_bufferSize) // allocate fm pipe buffer
private var _pitchTable = new Array[Int](2048)
private var _logTable = new Array[Int](6144)
private var _panTable:Array[Double] = new Array[Double](129)
init()
// Pass the buffer size and the def calls in each frame.
private def init() {
var i, j =0; var p, v=0.0; var t:Array[Int]=null; val ft=Array(0,1,2,3,4,5,6,7,7,6,5,4,3,2,1,0);
p=0; for (i <- 0 until 192){ // create pitchTable(128*16)
v=Math.pow(2, p)*12441.464342886; j=i;while(j<2048) {_pitchTable(j) = v.asInstanceOf[Int]; v*=2;j+=192}
p+=0.00520833333
}
for (i <- 0 until 32) _pitchTable(i) = (i+1)<<6 // (0:31) for white noize
i=0; p=0.0078125; while ( i<256) { // create logTable(12*256*2)
v=Math.pow(2, 13-p);j=i;while(j<3328){ _logTable(j)=v.asInstanceOf[Int]; _logTable(j+1) = -_logTable(j); v*=0.5; j+=256 }
i+=2; p+=0.0078125
}
for (i <- 3328 until 6144) _logTable(i) = 0; // (3328:6144) is 0-fill area
p=0;for (i<-0 until 129) {_panTable(i)=Math.sin(p)*0.5; p+=0.01217671571} // pan table;
t=Osc.createTable(10);p=0;for (i<-0 until 1024) {t(i) = Osc.log(Math.sin(p));p+=0.00613592315} // sin=0
t=Osc.createTable(10);p=0.75;for (i<-0 until 1024) {t(i) = Osc.log(p); p-=0.00146484375} // saw=1
t=Osc.createTable(5);for (i<-0 until 16) {t(i) = Osc.log(ft(i)*0.0625);t(i+16) = t(i) + 1} // famtri=2
t=Osc.createTable(15);for (i<-0 until 32768) t(i) = Osc.log(Osc.random()-0.5) // wnoize=3
for (i<-0 until 8) {t=Osc.createTable(4);for (j<-0 until 16) t(j) = if(j<=i) 192 else 193} // pulse=4-11
for (i<- 0 until _bufferSize) { _pipe(i)=0;_zero(i)=0 } // clear buffers
}
// reset all oscillators
def reset() { var o:Osc=Osc._tm.n;while(o!=Osc._tm) { o.fl = Osc._fl;o=o.inactivate().n } }
// Returns stereo output as Array[Double](_bufferSize*2).
def render() : Array[Double] = {
var i, j, ph, dph, mod, sh, tl, lout, v = 0
var osc:Osc = null; var tm:Osc = null; var l, r = 0.0;
var wv:Array[Int] = null; var fm:Array[Int] = null; var base:Array[Int] = null
var out=_pipe; var lt=_logTable; var stereoOut = _output; var imax = _bufferSize<<1
for (i <- 0 until imax) stereoOut(i) = 0
imax=_callbackFrams; while (imax<=_bufferSize) {
if (_onSoundFrame!=null) _onSoundFrame()
tm = Osc._tm; osc=tm.n; while (osc!=tm) {
dph=_pitchTable(osc.pt); ph=osc.ph; mod=osc.mod+10; sh=osc.sh; tl=osc.tl; wv=osc.wv
fm=if(osc.mod==0)_zero else _pipe; base=if (osc.out!=2)_zero else _pipe
for (i <- imax-_callbackFrams until imax) {
v = ((ph + (fm(i) << mod))& 0x3ffffff) >> sh
lout = wv(v) + tl; out(i) = lt(lout) + base(i);ph = (ph + dph) & 0x3ffffff
}
osc.ph = ph; if (osc.out==0) {
l = _panTable(64-osc.pan)// * 0.0001220703125
r = _panTable(64+osc.pan)// * 0.0001220703125
var k = imax-_callbackFrams;j=k*2; for (i<-k until imax) {
stereoOut(j) += out(i)*l; j += 1
stereoOut(j) += out(i)*r; j += 1
}
}; osc=osc.update()
}; imax+=_callbackFrams
}
stereoOut
}
// note on
def noteOn(pitch:Int) : Osc = noteOn(pitch,0,0,0,6,0,0)
def noteOn(pitch:Int, length:Int) : Osc = noteOn(pitch,length,0,0,6,0,0)
def noteOn(pitch:Int, length:Int, vol:Double) : Osc = noteOn(pitch,length,vol,0,6,0,0)
def noteOn(pitch:Int, length:Int, vol:Double, wave:Int) : Osc = noteOn(pitch,length,vol,wave,6,0,0)
def noteOn(pitch:Int, length:Int, vol:Double, wave:Int, decay:Int) : Osc = noteOn(pitch,length,vol,wave,decay,0,0)
def noteOn(pitch:Int, length:Int, vol:Double, wave:Int, decay:Int, sweep:Int) : Osc = noteOn(pitch,length,vol,wave,decay,sweep,0)
def noteOn(pitch:Int, length:Int, vol:Double, wave:Int, decay:Int, sweep:Int, pan:Int) : Osc = {
var osc:Osc = Osc.alloc().reset(); osc.pt = pitch; osc.len = length; osc.tl = Osc.log(vol)
osc.ws=wave; osc.dr = decay<<2; osc.sw = sweep; osc.pan = pan; osc.activate(true)
}
}
object Osc {
private val rnd = new Random
def random():Double = rnd.nextDouble()
// calculate index of logTable
def log(n:Double) : Int = {
if (n<0) {if(n< -0.00390625) {((1 + (Math.log(-n) * -184.66496523 + 0.5).asInstanceOf[Int]) << 1) + 1} else 2047}
else {if(n> 0.00390625) { (1 + (Math.log( n) * -184.66496523 + 0.5).asInstanceOf[Int]) << 1 } else 2046}
}
// create new wave table and you can refer the table by '@' command.
def createTable(b:Int) : Array[Int] = { _w.push(new Array[Int](1<<b)); _s.push (26 - b); _w.top }
var _w=new Stack[Array[Int]]; var _s=new Stack[Int]
var _fl=new Osc; var _tm=new Osc
def alloc():Osc = { if(_fl.p==_fl) new Osc else { var r:Osc=_fl.p;_fl.p=r.p;r.p.n=_fl; r } }
}
class Osc {
def into(x:Osc):Osc = { p=x.p;n=x;p.n=this;n.p=this; this }
var p, n = this; var fl:Osc = null; var pt, len, ph, tl, sw, dr, sh, mod, out, pan = 0
var wv:Array[Int]=null
def ws = 0; def ws_= (t:Int) { wv=Osc._w(t); sh=Osc._s(t) }
def update(): Osc = { tl+=dr; pt+=sw; pt&=2047; len-=1; if(len==0||tl>3328) inactivate().n else n }
def reset(): Osc = { ph=0; pt=0; len=0; tl=3328; sw=0; dr=24; pan=0; ws=0; mod=0; out=0; this }
def activate(autoFree:Boolean): Osc = { into(Osc._tm); fl=if(autoFree) Osc._fl else null; this }
def inactivate(): Osc = { tl=3328; if(fl==null) this else { var r:Osc=p; p.n=n; n.p=p; into(fl); r } }
def isActive(): Boolean = { tl<3328 }
}
-dontusemixedcaseclassnames
-dontoptimize
-injars oscp.jar;'c:/Program Files/Scala/lib/scala-library.jar'(!META-INF/MANIFEST.MF,!library.properties)
-outjars osc.jar
-libraryjars <java.home>/lib/rt.jar
-keep public class osc.oscApplet
call fsc osc.scala
jar cvfM oscp.jar osc/*.class
java -jar proguard.jar @osc.txt
copy /Y osc.html bin\.
copy /Y osc.jar bin\.
call app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment