diff --git a/SLIPDecoder.sc b/SLIPDecoder.sc index 0f3d0e8..be237c7 100644 --- a/SLIPDecoder.sc +++ b/SLIPDecoder.sc @@ -1,196 +1,242 @@ SLIPDecoder { - // SLIP DECODING - // =================== - // - // The packets are SLIP encoded using these special characters: - // end = 8r300 (2r11000000 or 0xc0 or 192) - // esc = 8r333 (2r11011011 or 0xdb or 219) - // esc_end = 8r334 (2r011011100 or 0xdc or 220) - // esc_esc = 8r335 (2r011011101 or 0xdd or 221) - // original code by Martin Marier & Fredrik Olofsson - const slipEND = 0xC0; - const slipESC = 0xDB; - const slipESC_END = 0xDC; - const slipESC_ESC = 0xDD; + // SLIP DECODING + // =================== + // + // The packets are SLIP encoded using these special characters: + // end = 8r300 (2r11000000 or 0xc0 or 192) + // esc = 8r333 (2r11011011 or 0xdb or 219) + // esc_end = 8r334 (2r011011100 or 0xdc or 220) + // esc_esc = 8r335 (2r011011101 or 0xdd or 221) + // original code by Martin Marier & Fredrik Olofsson + const slipEND = 0xC0; + const slipESC = 0xDB; + const slipESC_END = 0xDC; + const slipESC_ESC = 0xDD; - // OSC bundle header "#bundle" - const oscBundleHeader = #[35, 98, 117, 110, 100, 108, 101, 0]; + // OSC DECODING + // ============ + // bundle header "#bundle" + // type separator "," + const oscBundleHeader = #[35, 98, 117, 110, 100, 108, 101, 0]; + const oscTypeSeparator = 0x2C; - // OSC type separator "," - const oscTypeSeparator = 0x2C; + // Buffer for OSC bundles/messages + const bufferSize = 4096; - var deviceName, <>prependAddress, <>rate, <>port, decode, <>actions, firstRead, trace, stop, <>reader; + var <>deviceName, <>rate, <>prepend, = 0x20) && (x <= 0x7E), - { ^x.asAscii }, - { ^x }) - } + int2chr { |x| + // ce je stevilo, v razponu crk, stevk in znakov, vrni char, sicer kar cifro + if (x.isInteger && (x >= 0x20) && (x <= 0x7E), + { ^x.asAscii }, + { ^x }) + } - initSLIPDecoder { |deviceName,rate,prepend| - port = SerialPort(deviceName, rate); - prependAddress = prepend; - actions = []; // Each action is a function that takes the message contents as an argument. see 'decode' below. - firstRead = false; - trace = false; - stop = false; - } + init { + "init".postln; + dump(this); + deviceName.postln; + rate.postln; + port = SerialPort(deviceName, rate); + trace = false; + stop = false; + "init done".postln; + } - trace { |val| - trace = val; - } + trace { |val| + trace = val; + val; + } - // Function for decoding the properly-SLIP-decoded message. - decode { |data| - // Is it a bundle? (read header from first 8 bytes) - var header = data.at((0..7)); + traceMsg { |...msg| + if (trace, { "> ".post; msg.join(' ').postln; }); + } - if (header == oscBundleHeader, - // OSC bundle - { - var timetag, nextMsgLen, nextMsgStart, nextMsgEnd, bundlePart; + // Function for decoding the properly-SLIP-decoded message. + decode { |data| + // Is it a bundle? (read header from first 8 bytes) + var header = data.at((0..7)); - if (trace, { - "".postln; - "======".postln; - "BUNDLE".postln; - "======".postln; - }); + if (header == oscBundleHeader, + // OSC bundle + { + var timetag, nextMsgLen, nextMsgStart, nextMsgEnd, bundlePart; - // Next 8 bytes are a time tag (or null) - /* @TODO handle timetags! - timetag = data.at((8..15)); - */ + this.traceMsg("BUNDLE"); - // First message starts after the time tag - nextMsgStart = 16; + // Next 8 bytes are a time tag (or null) + /* @TODO handle timetags! + timetag = data.at((8..15)); + */ - // Loop for each message - while({ nextMsgStart < data.size }, - { - // Further 4 bytes hold the next message length - nextMsgLen = this.readInt32(data.at((nextMsgStart..(nextMsgStart + 3)))); - nextMsgStart = nextMsgStart + 4; - nextMsgEnd = nextMsgStart + nextMsgLen - 1; - bundlePart = data.at((nextMsgStart..nextMsgEnd)); + // First message starts after the time tag + nextMsgStart = 16; - // Recursively decode; each bundle part can be another bundle - this.decode(bundlePart); + // Loop for each message + while({ nextMsgStart < data.size }, + { + // Further 4 bytes hold the next message length + nextMsgLen = this.readInt32(data.at((nextMsgStart..(nextMsgStart + 3)))); + nextMsgStart = nextMsgStart + 4; + nextMsgEnd = nextMsgStart + nextMsgLen - 1; + bundlePart = data.at((nextMsgStart..nextMsgEnd)); - nextMsgStart = nextMsgEnd + 1; - } - ); - }, - // OSC message - { - var nextString, index, address, type, args = []; + // Recursively decode; each bundle part can be another bundle + this.decode(bundlePart); - // Message Address - nextString = this.readNextString(data); - address = nextString[0]; - data = data[(nextString[1]..(data.size - 1))]; + nextMsgStart = nextMsgEnd + 1; + } + ); + }, + // OSC message + { + var nextString, index, address, type, args = []; - nextString = this.readNextString(data); - type = nextString[0]; - data = data[(nextString[1]..(data.size - 1))]; + // Message Address + nextString = this.readNextString(data); + address = nextString[0]; + data = data[(nextString[1]..(data.size - 1))]; - type.do({ |t| - t.switch ( - $i, { - args = args.add(this.readInt32(data)); - data = data[(4..(data.size - 1))]; - }, - $f, { - args = args.add(this.readFloat32(data)); - data = data[(4..(data.size - 1))]; - }, - $s, { - nextString = this.readNextString(data); - args = args.add(nextString[0]); - data = data[(nextString[1]..(data.size - 1))]; - } - /* @TODO implement strings, bytearrays */ - ); - }); + nextString = this.readNextString(data); + type = nextString[0]; + data = data[(nextString[1]..(data.size - 1))]; - // Send OSC message to the engine - NetAddr.localAddr.sendMsg(prependAddress ++ address, *args); - } - ); - } + type.do({ |t| + t.switch ( + $i, { + args = args.add(this.readInt32(data)); + data = data[(4..(data.size - 1))]; + }, + $f, { + args = args.add(this.readFloat32(data)); + data = data[(4..(data.size - 1))]; + }, + $s, { + nextString = this.readNextString(data); + args = args.add(nextString[0]); + data = data[(nextString[1]..(data.size - 1))]; + } + /* @TODO implement strings, bytearrays */ + ); + }); + this.traceMsg("OSC", prepend ++ address, args); - start { - stop = false; - reader = fork { - var buffer, serialByte; + // Send OSC message to the engine + NetAddr.localAddr.sendMsg(prepend ++ address, *args); + } + ); + } - var bufferSize = 1024; // 1KB - buffer = Int8Array(maxSize: bufferSize); - while({stop.not}, { - serialByte = port.read; - serialByte.switch( - slipEND, { - if (firstRead && buffer.isEmpty.not, - { - this.decode(buffer); - }, - { firstRead = true; } - ); - buffer = Int8Array(maxSize: bufferSize); - }, - slipESC, { - serialByte = port.read; - serialByte.switch( - slipESC_END, { buffer.add(slipEND) }, - slipESC_ESC, { buffer.add(slipESC) }, - {"SLIP encoding error.".error } - ) - }, - { buffer.add(serialByte) } - ); + start { + this.traceMsg("Starting..."); + dump(this); + // TODO fix restart + if ((port == nil), {this.init;}); + if (port.isOpen.not, {this.init;}); + + this.traceMsg("opened"); + + reader = fork { + var serialByte, buffer, firstRead; + firstRead = true; + + // Skip data before the first END character + while({stop.not && firstRead}, { + serialByte = port.read; + + //this.traceMsg("Read byte"); + //this.traceMsg(serialByte.asAscii); + + if (serialByte == slipEND, { + buffer = Int8Array(maxSize: bufferSize); + firstRead = false; + }, { + //this.traceMsg("Skip...") + }); + }); + + // Start reading data + this.traceMsg("First read!"); + while({stop.not}, { + serialByte = port.read; + //this.traceMsg("Checking", serialByte.asInteger, serialByte.asAscii); + serialByte.switch( + // on END, decode buffer + slipEND, { + //this.traceMsg("SLIP END, decoding "); + //this.traceMsg(buffer); + //3.wait; + if (buffer.isEmpty.not, { + this.traceMsg("decode!", buffer); + this.decode(buffer); + buffer = Int8Array(maxSize: bufferSize); }); - }; - } + }, + slipESC, { + serialByte = port.read; + serialByte.switch( + slipESC_END, { + //this.traceMsg("SLIP ESC and ESC_END"); + buffer.add(slipEND) + }, + slipESC_ESC, { + //this.traceMsg("SLIP ESC and ESC_ESC"); + buffer.add(slipESC) + }, + {"SLIP encoding error.".error } + ); + }, + { + // Otherwise just add the byte + //this.traceMsg(buffer); + buffer.add(serialByte); + }); + }); + }; + } - stop { reader.stop } + stop { + this.traceMsg("Stopping..."); + stop = true; + port.close; + } }