use tksampler;
use tkmidi;
boolean b_debug = false;
boolean b_upload = true;
class Utils {
static SplitPathname(String name, path, file) {
int idx = name.lastIndexOf("/");
int idxDos = name.lastIndexOf("\\");
if(idxDos > idx)
{
idx = idxDos;
}
if(-1 != idx)
{
name.substring(0, idx) => path;
name.substring(idx+1, -1) => file;
}
else
{
path = null;
file = name;
}
}
}
String inDevName = "Elektron Analog Rytm";
String outDevName = "Elektron Analog Rytm";
int sysexChannel = 0;
function Usage() {
trace "[...] Usage: tks app:sds [-chain <length>] [-varichain] [-autodir] [-dir <dirprefix>] <filelist.txt> [-single <file.wav>] [-name <samplename>] [-pad <chainpadsize>] [-minpad <varichainminpadsize>] [-d]";
exit(5);
}
if(Arguments.numElements < 1)
{
Usage();
}
String single_filename = "";
int chain_size = 0;
boolean b_varichain = false;
boolean b_chain = false;
int argIdx = 0;
String filelistFileName = "";
String dir_prefix = "";
boolean b_autodir = false;
String force_sample_name = "";
int extra_padding = 0;
int min_padding = 0;
while(argIdx < Arguments.numElements)
{
switch(Arguments[argIdx])
{
default:
filelistFileName = Arguments[argIdx];
break;
case "-dir":
dir_prefix = Arguments[argIdx] + "/";
b_autodir = false;
trace "[dbg] Manual dir prefix set to \""+dir_prefix+"\".";
break;
case "-autodir":
b_autodir = true;
trace "[dbg] Enable automatic dir prefix";
break;
case "-single":
if( (argIdx+1) < Arguments.numElements)
{
single_filename = Arguments[++argIdx];
trace "[dbg] Upload single file, name=\""+single_filename+"\"";
}
else Usage();
break;
case "-chain":
if( (argIdx+1) < Arguments.numElements)
{
chain_size = Arguments[++argIdx];
if(1 <= chain_size <= 120)
{
if(120 == ((120 / chain_size) * chain_size))
{
b_chain = true;
trace "[dbg] Enable fixed length chain mode, length="+chain_size+" => step="+(120/chain_size);
}
else
{
trace "[dbg] Invalid chain size ("+chain_size+"): (120 / "+chain_size+") is not an integer.";
Usage();
}
}
else
{
trace "[---] invalid chain size \""+chain_size+"\" (the valid range is 1..120)";
Usage();
}
}
else Usage();
break;
case "-varichain":
b_varichain = true;
b_chain = true;
trace "[dbg] Enable varichain mode";
break;
case "-noupload":
trace "[dbg] Disable all uploads";
b_upload = false;
break;
case "-name":
if( (argIdx+1) < Arguments.numElements)
{
force_sample_name = Arguments[++argIdx];
trace "[dbg] single/chain sample name set to \""+force_sample_name+"\"";
}
else Usage();
break;
case "-d":
b_debug = true;
break;
case "-pad":
if( (argIdx+1) < Arguments.numElements)
{
extra_padding = Arguments[++argIdx];
if(0 <= extra_padding <= (1024*1024))
{
trace "[dbg] extra chain padding set to \""+extra_padding+"\" sample fragments";
}
else
{
trace "[---] invalid pad size ("+extra_padding+") (the valid range is 0..1048576).";
exit(5);
}
}
else Usage();
break;
case "-minpad":
if( (argIdx+1) < Arguments.numElements)
{
min_padding = Arguments[++argIdx];
if(0 <= min_padding <= (1024*1024))
{
trace "[dbg] min chain padding set to \""+min_padding+"\" sample fragments";
}
else
{
trace "[---] invalid min pad size ("+min_padding+") (the valid range is 0..1048576).";
exit(5);
}
}
else Usage();
break;
}
argIdx++;
}
if(single_filename.isBlank())
{
if(!filelistFileName.isBlank())
{
String filelist;
if(!filelist.loadLocal(filelistFileName, true))
{
trace "[---] failed to load filelist from \""+filelistFileName+"\".";
exit(5);
}
}
else
{
trace "[---] please set the filelist filename";
Usage();
}
}
MIDIIn midiin;
MIDIOut midiout;
int devIdx;
if(b_debug)
{
trace "[dbg] found "+MIDIIn.GetNumDevices()+" input devices:";
devIdx = 0;
loop(MIDIIn.GetNumDevices())
{
trace "[dbg] ["+devIdx+"]: \""+MIDIIn.GetDeviceNameByIdx(devIdx)+"\".";
devIdx++;
}
trace "";
}
if(b_debug)
{
trace "[dbg] found "+MIDIOut.GetNumDevices()+" output devices:";
devIdx = 0;
loop(MIDIOut.GetNumDevices())
{
trace "[dbg] ["+devIdx+"]: \""+MIDIOut.GetDeviceNameByIdx(devIdx)+"\".";
devIdx++;
}
}
class SDS_Test {
Buffer in_buf;
Buffer out_buf;
boolean b_ignore_waits = true;
boolean b_waitack = false;
boolean b_allow_resend = false; int packet_sleep_interval = 23; int packet_sleep_ms = 5; IntArray ack_buf;
protected StSample *sam;
protected StWaveform *wav;
protected FloatArray *dat;
protected short sample_nr;
protected byte sysex_channel;
protected int packet_nr;
protected int num_packets;
define int STATE_TIMEOUT = -1;
define int STATE_WAIT = 0x7C;
define int STATE_CANCEL = 0x7D;
define int STATE_NAK = 0x7E;
define int STATE_ACK = 0x7F;
protected method sendDumpHeader() {
out_buf.offset = 0;
out_buf.i8 = 0xF0;
out_buf.i8 = 0x7E;
out_buf.i8 = sysex_channel;
out_buf.i8 = 0x01;
out_buf.i8 = (sample_nr & 127);
out_buf.i8 = (sample_nr >> 7) & 127;
out_buf.i8 = 16; float rateNanoSec = (1000000.0 / (wav.sampleRate / 1000.0f));
out_buf.i8 = int(rateNanoSec) & 127;
out_buf.i8 = (int(rateNanoSec) >> 7) & 127;
out_buf.i8 = (int(rateNanoSec) >> 14) & 127;
int numWords = dat.numElements;
out_buf.i8 = numWords & 127;
out_buf.i8 = (numWords >> 7) & 127;
out_buf.i8 = (numWords >> 14) & 127;
int loopStart = numWords;
out_buf.i8 = loopStart & 127;
out_buf.i8 = (loopStart >> 7) & 127;
out_buf.i8 = (loopStart >> 14) & 127;
int loopEnd = numWords;
out_buf.i8 = loopEnd & 127;
out_buf.i8 = (loopEnd >> 7) & 127;
out_buf.i8 = (loopEnd >> 14) & 127;
out_buf.i8 = 0x7F; out_buf.i8 = 0xF7;
midiout.sendBuffer(out_buf);
}
protected method sendDumpHeaderExt() {
if(b_debug)
{
trace "[dbg] sendDumpHeaderExt";
}
out_buf.offset = 0;
out_buf.i8 = 0xF0;
out_buf.i8 = 0x7E;
out_buf.i8 = sysex_channel;
out_buf.i8 = 0x05; out_buf.i8 = 0x05; out_buf.i8 = (sample_nr & 127);
out_buf.i8 = (sample_nr >> 7) & 127;
out_buf.i8 = 16; int srHzInt = wav.sampleRate;
out_buf.i8 = srHzInt & 127;
out_buf.i8 = (srHzInt >> 7) & 127;
out_buf.i8 = (srHzInt >> 14) & 127;
out_buf.i8 = (srHzInt >> 21) & 127;
int srHzFrac = frac(wav.sampleRate) * 10000000;
out_buf.i8 = srHzFrac & 127;
out_buf.i8 = (srHzFrac >> 7) & 127;
out_buf.i8 = (srHzFrac >> 14) & 127;
out_buf.i8 = (srHzFrac >> 21) & 127;
int numWords = dat.numElements;
out_buf.i8 = numWords & 127;
out_buf.i8 = (numWords >> 7) & 127;
out_buf.i8 = (numWords >> 14) & 127;
out_buf.i8 = (numWords >> 21) & 127;
out_buf.i8 = (numWords >> 28) & 127;
int loopStart = numWords;
out_buf.i8 = loopStart & 127;
out_buf.i8 = (loopStart >> 7) & 127;
out_buf.i8 = (loopStart >> 14) & 127;
out_buf.i8 = (loopStart >> 21) & 127;
out_buf.i8 = (loopStart >> 28) & 127;
int loopEnd = numWords;
out_buf.i8 = loopEnd & 127;
out_buf.i8 = (loopEnd >> 7) & 127;
out_buf.i8 = (loopEnd >> 14) & 127;
out_buf.i8 = (loopEnd >> 21) & 127;
out_buf.i8 = (loopEnd >> 28) & 127;
out_buf.i8 = 0; out_buf.i8 = wav.numChannels;
out_buf.i8 = 0xF7;
}
public method enableElektronARTurbo() {
out_buf.offset = 0;
out_buf.i8 = 0xF0;
out_buf.i8 = 0x00;
out_buf.i8 = 0x20;
out_buf.i8 = 0x3C;
out_buf.i8 = 0x04;
out_buf.i8 = 0x00;
out_buf.i8 = 0x02;
out_buf.i8 = 0x08;
out_buf.i8 = 0xF7;
midiout.sendBuffer(out_buf);
}
static public GetStateName(int state) : String {
switch(state)
{
default:
return "<UNKNOWN>";
case STATE_TIMEOUT:
return "<TIMEOUT>";
case STATE_WAIT:
return "WAIT";
case STATE_CANCEL:
return "CANCEL";
case STATE_ACK:
return "ACK";
case STATE_NAK:
return "NAK";
}
}
protected method waitAck(Integer _retPacketNr, boolean _bForceWait) : int {
explain "Wait for ACK from sampler. Returns state 0=ACK, 1=NAK, 2=CANCEL, 3=WAIT.";
int timeout = milliSeconds() + 2000;
int numTimeouts = 0;
return = 0;
int numWaits = 0;
RecordedMIDIEvent *ev;
boolean bHaveAck = false;
while(!bHaveAck)
{
ev <= midiin.nextEvent;
if(null == ev)
{
ev <= midiin.waitNextEvent(100);
}
if(null != ev)
{
if(ev.isLongMessage())
{
if(4 == ev.size)
{
in_buf.offset = 0;
ev.copyToStream(in_buf);
if(0x7E == in_buf[0])
{
return = in_buf[2];
_retPacketNr = in_buf[3]; if(b_debug)
{
trace "[dbg] ok, recv'd state "+GetStateName(in_buf[2])+". packetNr="+_retPacketNr;
}
if(STATE_NAK == in_buf[2])
{
trace "[~~~] NAK, resending packet "+packet_nr+"/"+num_packets;
sendNextPacket();
}
else if(STATE_ACK == in_buf[2])
{
ack_buf[_retPacketNr] = true;
bHaveAck = (_retPacketNr == (packet_nr & 127));
}
else if(STATE_WAIT == in_buf[2])
{
if(b_ignore_waits && !_bForceWait)
{
}
else
{
numWaits++;
if(numWaits > 1)
{
if(b_debug)
{
trace "[dbg] waiting..";
}
TKS.sleep(20);
}
if(numWaits > 500)
{
trace "[~~~] more than 500 WAIT replies recv'd";
break;
}
}
}
else
{
trace "[~~~] ***** CANCEL ****";
return 2;
}
}
}
}
}
if(milliSeconds() >= timeout)
{
numTimeouts++;
trace "[~~~] SDS::waitAck: timeout after 2 sec (#timeouts="+numTimeouts+")";
if(numTimeouts >= 15)
{
return STATE_TIMEOUT;
}
timeout = milliSeconds() + 2000;
}
}
if(b_ignore_waits && !_bForceWait)
do
{
ev <= midiin.nextEvent;
if(null != ev)
{
in_buf.offset = 0;
ev.copyToStream(in_buf);
if(0x7E == in_buf[0])
{
if(STATE_ACK == in_buf[2])
{
int packetNr = in_buf[3];
ack_buf[packetNr] = true;
if(b_debug)
{
trace "[dbg] <queue> ok, recv'd state "+GetStateName(in_buf[2])+". packetNr="+packetNr;
}
}
else if(STATE_WAIT == in_buf[2])
{
if(b_debug)
{
trace "[dbg] <queue> ok, recv'd state "+GetStateName(in_buf[2])+". packetNr="+packetNr;
}
}
else if(STATE_NAK == in_buf[2])
{
trace "[~~~] NAK, resending packet "+packet_nr+"/"+num_packets;
sendNextPacket();
}
else if(STATE_CANCEL == in_buf[2])
{
return 2;
}
}
}
} while(null != ev);
}
protected method sendNextPacket() {
if( b_debug )
{
trace "[dbg] sending packet #"+packet_nr+" / "+num_packets+" (modpn="+(packet_nr&127)+")";
}
else
{
stdout ".";
}
out_buf.offset = 0;
out_buf.i8 = 0xF0;
out_buf.i8 = 0x7E;
out_buf.i8 = sysex_channel;
out_buf.i8 = 0x02;
out_buf.i8 = (packet_nr & 127);
int chksum = 0x7E ^ sysex_channel ^ 0x02 ^ (packet_nr & 127);
int smpOff = packet_nr * (120 / 3);
loop(120/3)
{
int smp = int(dat.get(smpOff) * 32767) + 0x8000;
byte b1 = (smp >> 9) & 127;
byte b2 = (smp >> 2) & 127;
byte b3 = (smp & 3);
chksum = chksum ^ b1 ^ b2 ^ b3;
out_buf.i8 = b1;
out_buf.i8 = b2;
out_buf.i8 = b3;
smpOff++;
}
out_buf.i8 = (chksum & 127);
out_buf.i8 = 0xF7;
midiout.sendBuffer(out_buf);
}
public method upload(StSample _sam, short _sampleNr, byte _sysexChannel, String _nameOrNull) : boolean {
in_buf.size = 4096;
out_buf.size = 4096;
ack_buf.alloc(128);
ack_buf.numElements = 128;
ack_buf.fill(false);
sam <= _sam;
wav <= sam.waveform;
dat <= wav.sampleData;
sample_nr = _sampleNr; sysex_channel = _sysexChannel;
if(null != _nameOrNull)
{
sendDumpHeaderExt();
sendSampleNameExtPriv(_nameOrNull);
if(b_debug)
{
File f; f.openLocal("dbghdr.dat", IOS_OUT);
f.writeBuffer(out_buf, 0, out_buf.offset);
f.close();
}
midiout.sendBuffer(out_buf);
}
else
{
sendDumpHeader();
}
Integer packetNrRecv;
num_packets = ((dat.numElements + 39) / (120/3));
packet_nr = 0;
loop(num_packets)
{
sendNextPacket();
if(b_waitack)
{
if(2 == waitAck(packetNrRecv, false))
{
return false;
}
}
packet_nr++;
if(0 == (packet_nr & 127))
{
if(-1 != ack_buf.indexOf(0, 0))
{
if(b_allow_resend)
{
if(b_debug)
{
trace "[dbg] wait ack all buf="+ack_buf.string;
}
packet_nr -= 128;
int acki = 0;
loop(128)
{
if(0 == ack_buf[acki])
{
if(b_debug)
{
trace "[dbg] resend packet "+packet_nr+"/"+num_packets;
}
sendNextPacket();
if(2 == waitAck(packetNrRecv, true))
{
return false;
}
}
acki++;
packet_nr++;
}
}
else
{
if(b_debug)
{
trace "[~~~] not all packets ack'd buf="+ack_buf.string;
}
}
}
ack_buf.fill(false);
}
if(0 != packet_sleep_interval)
{
if(0 == (packet_nr % packet_sleep_interval))
{
TKS.sleep(packet_sleep_ms);
}
}
}
trace "";
return true;
}
protected method sendSampleNameExtPriv(String _name) {
if(b_debug)
{
trace "[dbg] sendSampleNameExt";
}
int nlen = _name.length;
if(nlen > 0)
{
nlen--; if(nlen > 127)
nlen = 127;
out_buf.i8 = 0xF0;
out_buf.i8 = 0x7E;
out_buf.i8 = sysex_channel;
out_buf.i8 = 5; out_buf.i8 = 3; out_buf.i8 = (sample_nr & 127);
out_buf.i8 = (sample_nr >> 7) & 127;
out_buf.i8 = 0; out_buf.i8 = nlen; int ci = 0;
loop(nlen)
{
out_buf.i8 = _name.getc(ci++);
}
out_buf.i8 = 0xF7;
}
}
public method sendSampleNameExt(int _sampleNr, int _sysexCh, String _name) {
if(b_debug)
{
trace "[dbg] sendSampleNameExt";
}
sysex_channel = _sysexCh;
sample_nr = _sampleNr;
if(_name.length > 0)
{
out_buf.offset = 0;
sendSampleNameExtPriv(_name);
midiout.sendBuffer(out_buf);
}
}
}
StSample g_sam;
StWaveform g_wav;
Integer g_rate;
Integer g_numCh;
String g_fileInfo;
g_wav.alloc(1, 1);
g_sam.setWaveform(g_wav);
class ChainEntry {
StSample *sam;
StWaveform *wav;
StWaveform *twav;
Integer rate;
Integer numCh;
String fileInfo;
String fnPath;
String fnFile;
int orig_sz;
int pad_sz;
float slice_pct;
public method init() {
sam <= new StSample;
wav <= new StWaveform;
wav.alloc(1, 1);
sam.setWaveform(wav);
orig_sz = 1;
}
public method load(String _pathname) : boolean {
boolean ret = false;
if(WavIO.LoadLocal(_pathname, wav.sampleData, rate, numCh, fileInfo, sam))
{
if(b_debug)
{
trace "[dbg] wav.sampleData.numElements="+(wav.sampleData.numElements);
}
Utils.SplitPathname(_pathname, fnPath, fnFile);
fnFile.replace(".wav", "");
fnFile.replace(".WAV", "");
orig_sz = getSampleSize();
ret = true;
}
else
{
trace "[---] failed to open sample file \""+_pathname+"\".";
}
return ret;
}
public method getSampleSizeInBytes() : int {
return wav.sampleData.numElements * 2;
}
public method getSampleSize() : int {
return wav.sampleData.numElements;
}
public method upload() {
if(b_upload)
{
SDS_Test sds <= new SDS_Test;
int tStart = milliSeconds();
String remoteName = fnFile;
if(!single_filename.isBlank() || b_chain)
{
if(!force_sample_name.isBlank())
{
remoteName = force_sample_name;
}
}
sds.upload(sam, 0, sysexChannel, remoteName);
int tDelta = milliSeconds() - tStart;
trace "[...] file transfered in "+(tDelta/1000.0f)+" seconds ("+((2*wav.sampleData.numElements) / (tDelta/1000.0f))+" bytes/sec).";
}
else
{
trace "[...] skipping upload because of -noupload option";
}
}
public method resizeTo(int _sz) {
FloatArray dat <= wav.sampleData;
if(dat.numElements != _sz)
{
FloatArray nd;
nd.realloc(_sz);
nd.useAll();
nd.fill(0.0f);
nd.copyFrom(dat, 0, dat.numElements, 0);
dat = nd;
}
}
}
PointerArray chain; ChainEntry out_chain;
out_chain.init();
class ChainUtils {
static GetMaxSampleSize() : int {
int ret = 0;
ChainEntry *ch;
foreach ch in chain
{
int sz = ch.getSampleSize();
if(sz > ret)
ret = sz;
}
return ret;
}
static GetTotalSampleSize() : int {
int ret = 0;
ChainEntry *ch;
foreach ch in chain
{
ret += ch.getSampleSize();
}
return ret;
}
static GetTotalSampleSizeInBytes() : int {
int ret = 0;
ChainEntry *ch;
foreach ch in chain
{
ret += ch.getSampleSizeInBytes();
}
return ret;
}
static GetAvgSlicePad() : float {
float r = 0;
ChainEntry *ch;
foreach ch in chain
{
r += ch.pad_sz;
}
return r / chain.numElements;
}
static ResizeEntriesTo(int _sz) {
ChainEntry *ch;
foreach ch in chain
{
ch.resizeTo(_sz);
}
}
static AddPadding(int _pad) {
if(_pad > 0)
{
ChainEntry *ch;
foreach ch in chain
{
ch.resizeTo(ch.getSampleSize() + _pad);
ch.pad_sz = _pad;
}
}
}
static ArePadSizesGreaterThan(int _minPad) : boolean {
ChainEntry *ch;
foreach ch in chain
{
if(ch.pad_sz < _minPad)
return false;
}
return true;
}
static RestoreOrigSizes() : boolean {
ChainEntry *ch;
foreach ch in chain
{
ch.resizeTo(ch.orig_sz);
ch.pad_sz = 0;
}
}
static CalcSlicePct1(float _maxSmpSz) {
ChainEntry *ch;
foreach ch in chain
{
int chSz = ch.getSampleSize();
ch.slice_pct = chSz / _maxSmpSz;
}
}
static float cur_sta = 0; static AlignEntrySizesTo(int _sz) {
ChainEntry *ch;
foreach ch in chain
{
int chSz = ch.getSampleSize();
if(chSz != ((chSz / _sz) * _sz))
{
chSz = ((chSz / _sz) + 1) * _sz;
ch.pad_sz = chSz - ch.orig_sz;
Float ioSta = cur_sta;
Integer ioOrigSz = ch.orig_sz;
Integer ioPadSz = ch.pad_sz;
Integer ioChSz = chSz;
trace "[trc] STA="+ioSta.printf("%3.3f")+" origSz="+ioOrigSz.printf("%10u")+" padSz="+ioPadSz.printf("%8d")+" chSz="+ioChSz.printf("%10d")+" stepSz="+_sz;
ch.resizeTo(chSz);
}
cur_sta += (float(chSz) / _sz);
}
}
static AlignPaddedEntrySizesTo(int _sz) {
ChainEntry *ch;
foreach ch in chain
{
int chSz = ch.getSampleSize();
if(chSz != ((chSz / _sz) * _sz))
{
chSz = ((chSz / _sz) + 0) * _sz;
ch.pad_sz = chSz - ch.orig_sz;
Float ioSta = cur_sta;
Integer ioOrigSz = ch.orig_sz;
Integer ioPadSz = ch.pad_sz;
Integer ioChSz = chSz;
trace "[trc] STA="+ioSta.printf("%6.2f")+" origSz="+ioOrigSz.printf("%8u")+" padSz="+ioPadSz.printf("%8d")+" chSz="+ioChSz.printf("%8d")+" stepSz="+_sz;
ch.resizeTo(chSz);
}
cur_sta += (float(chSz) / _sz);
}
}
static BuildOutChain() {
FloatArray d <= out_chain.wav.sampleData;
ChainEntry *ch;
foreach ch in chain
{
trace "xxx ch.sz="+((ch.wav.sampleData.numElements)*2);
d.join(d, ch.wav.sampleData);
}
}
}
if(b_autodir)
{
String fnPathDir;
String fnFileDir;
Utils.SplitPathname(filelistFileName, fnPathDir, fnFileDir);
dir_prefix = fnPathDir + "/";
}
if(!b_upload || midiin.openByName(inDevName))
{
if(b_upload)
midiin.start();
if(!b_upload || midiout.openByName(outDevName))
{
String fileName;
int fileNr = 1;
StringArray fileNames;
if(single_filename.isBlank())
{
fileNames <= filelist.splitChar('\n');
}
else
{
fileNames.add(single_filename);
}
foreach fileName in fileNames
{
fileName.trim();
fileName = dir_prefix+fileName;
if(b_chain)
{
ChainEntry en <= new ChainEntry;
en.init();
if(en.load(fileName))
{
trace "[trc] add chain entry \""+fileName+"\"";
chain.add(#(deref en));
}
}
else
{
if(fileNames.numElements > 1)
{
force_sample_name = "";
}
if(WavIO.LoadLocal(fileName, g_wav.sampleData, g_rate, g_numCh, g_fileInfo, g_sam))
{
if(b_debug)
{
trace "[dbg] wav.sampleData.numElements="+(g_wav.sampleData.numElements);
}
if(b_upload)
{
SDS_Test sds <= new SDS_Test;
int tStart = milliSeconds();
String g_fnPath;
String g_fnFile;
Utils.SplitPathname(fileName, g_fnPath, g_fnFile);
g_fnFile.replace(".wav", "");
g_fnFile.replace(".WAV", "");
String g_remoteName = g_fnFile;
if(!single_filename.isBlank())
{
if(!force_sample_name.isBlank())
{
g_remoteName = force_sample_name;
}
}
sds.upload(g_sam, 0, sysexChannel, g_remoteName);
int tDelta = milliSeconds() - tStart;
trace "[...] file "+fileNr+"/"+(fileNames.numElements)+" transfered in "+(tDelta/1000.0f)+" seconds ("+((2*g_wav.sampleData.numElements) / (tDelta/1000.0f))+" bytes/sec).";
}
}
else
{
trace "[---] failed to open sample file \""+fileName+"\".";
}
} fileNr++;
} if(b_chain)
{
if(chain.numElements > 0)
{
int maxSmpSz;
if(0 != chain_size)
{
if(chain.numElements > chain_size)
{
trace "[~~~] warning: number of files ("+chain.numElements+") exceeds the chain size ("+chain_size+"), some files will not be uploaded!";
}
else if(chain.numElements < chain_size)
{
trace "[...] chain size ("+chain_size+") exceeds the number of files ("+chain.numElements+"), adding silence to compensate";
loop(chain_size - chain.numElements)
{
ChainEntry padChEn <= new ChainEntry;
padChEn.init();
chain.add(#(deref padChEn));
}
}
maxSmpSz = ChainUtils.GetMaxSampleSize();
ChainUtils.ResizeEntriesTo(maxSmpSz + extra_padding);
}
else if(b_varichain)
{
int origTotalSmpSz = ChainUtils.GetTotalSampleSize();
trace "[...] origTotalSmpSz="+origTotalSmpSz;
if(extra_padding < 2000)
extra_padding = 2000;
int iter = 1;
for(;;)
{
ChainUtils.AddPadding(extra_padding);
int varStepSmpSz;
int chainStep;
int totalSmpSz;
chainStep = 120 / chain.numElements;
trace "xxx chainStep="+chainStep;
totalSmpSz = ChainUtils.GetTotalSampleSize();
int origPadTotalSmpSz = totalSmpSz;
trace "xxx start totalSmpSz="+totalSmpSz;
maxSmpSz = ChainUtils.GetMaxSampleSize();
trace "xxx start maxSmpSz="+maxSmpSz;
ChainUtils.CalcSlicePct1(maxSmpSz);
float maxPct = float(maxSmpSz) / totalSmpSz;
int maxNumSlices = (120 * maxPct) + 0.5;
trace "xxx maxPct="+maxPct+" maxNumSlices="+maxNumSlices;
float slcSz = maxSmpSz / maxNumSlices;
trace "xxx slcSz="+slcSz;
ChainUtils.cur_sta = 0;
ChainUtils.AlignEntrySizesTo(slcSz);
totalSmpSz = ChainUtils.GetTotalSampleSize();
trace "[...] newTotalSmpSz="+totalSmpSz;
float newNumSlices = totalSmpSz / slcSz;
trace "[...] newNumSlices="+newNumSlices+" int="+int(newNumSlices+0.5);
slcSz = totalSmpSz / 120.0;
trace "[...] newSlcSz="+slcSz+" int="+int(slcSz+0.5);
slcSz = int(slcSz+0.5);
ChainUtils.cur_sta = 0;
ChainUtils.AlignPaddedEntrySizesTo(slcSz);
if(ChainUtils.ArePadSizesGreaterThan(min_padding))
break;
else
{
ChainUtils.RestoreOrigSizes();
extra_padding += 100;
iter++;
}
}
totalSmpSz = ChainUtils.GetTotalSampleSize();
float padNewNumSlices = totalSmpSz / slcSz;
trace "[...] padNewNumSlices="+padNewNumSlices+" int="+int(padNewNumSlices+0.5);
ChainEntry enVariPad <= new ChainEntry;
enVariPad.init();
chain.add(#(deref enVariPad));
enVariPad.resizeTo( (120 - padNewNumSlices) * slcSz );
totalSmpSz = ChainUtils.GetTotalSampleSize();
float finalNumSlices = totalSmpSz / slcSz;
trace "[...] finalNumSlices="+finalNumSlices+" int="+int(finalNumSlices+0.5);
trace "[...] origTotalSmpSz="+origTotalSmpSz;
trace "[...] total padding:"+(totalSmpSz - origTotalSmpSz)+" ratio to unpadded orig="+((float(totalSmpSz)/origTotalSmpSz)*100)+"%";
trace "[...] ratio to padded orig="+((float(totalSmpSz)/origPadTotalSmpSz)*100)+"%";
trace "[...] avg slice padding="+ChainUtils.GetAvgSlicePad();
totalSmpSz = ChainUtils.GetTotalSampleSize();
trace "[...] totalSmpSz="+totalSmpSz+" /120="+(float(totalSmpSz)/120);
trace "[...] ("+(totalSmpSz*2)+" bytes)";
trace "[dbg] num iterations="+iter;
}
else
{
trace "[---] chain is empty, nothing to upload.";
exit(5);
}
trace "[...] Building sample chain..";
ChainUtils.BuildOutChain();
if(out_chain.getSampleSizeInBytes() >= (16*1024*1024))
{
trace "[---] the sample chain is larger than 16 MBytes, please reduce the chain size or the number of samples.";
exit(5);
}
trace "[...] Sample chain memory size is "+float(out_chain.getSampleSizeInBytes() / 1024.0f)+" kBytes"; if(b_debug)
{
trace "[...] writing generated sample chain to \"debug.wav\"";
WavIO.SaveLocal("debug.wav", out_chain.wav.sampleData, out_chain.wav.sampleRate, 1);
}
out_chain.upload();
}
}
if(b_upload)
{
midiout.close();
if(b_debug)
{
trace "[dbg] closed MIDI device";
}
}
}
else
{
trace "[---] failed to open MIDI output device \""+outDevName+"\".";
}
if(b_upload)
{
midiin.stop();
midiin.close();
}
}
else
{
trace "[---] failed to open MIDI input device \""+inDevName+"\".";
}