Created
January 13, 2009 12:57
-
-
Save hsak/46435 to your computer and use it in GitHub Desktop.
FM Chip tube on Scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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