### Post by ki on Jan 12, 2019 14:42:03 GMT

McDTracy implemented a very cool script for harmonizing single notes, which he published in the AB forum. I started refactoring the code while he discussed the code further with Nic in this forum. There were so many ideas and i could not stop to completly rebuild and enhance the script.

My version of the ‚OneFingerChords’ script includes the following user specified features:

* 4 independant tunings for 4x12 chords, switchable via modwheel cc

* Chord-selection transposition and generated-note transposition

* Strumming per tuning, defined in note divisions (ie 1/16) and bpm

* Humanization factor for timing

*

Programming related features

* All generated notes stop immediately at the initiating keys note-off, even the creation of the associated/delayed strum notes is aborted

* Filters incomming double note on/offs

* Prevent generation of double note on/offs

* BCD (Binary-Coded-Digit) to hexadecimal conversion of 264 user parameters

* Fixed-point math (humanize) and big value div (bpm to msec)

* Very well documented / commented

My version of the ‚OneFingerChords’ script includes the following user specified features:

* 4 independant tunings for 4x12 chords, switchable via modwheel cc

* Chord-selection transposition and generated-note transposition

* Strumming per tuning, defined in note divisions (ie 1/16) and bpm

* Humanization factor for timing

*

**All user variables written in decimal instead of hex**Programming related features

* All generated notes stop immediately at the initiating keys note-off, even the creation of the associated/delayed strum notes is aborted

* Filters incomming double note on/offs

* Prevent generation of double note on/offs

* BCD (Binary-Coded-Digit) to hexadecimal conversion of 264 user parameters

* Fixed-point math (humanize) and big value div (bpm to msec)

* Very well documented / commented

`# OneFingerChords: Generate chord variants from single notes`

# Version: 11 / 12.01.2019

# Author: -ki https://forum.audiob.us/profile/_ki

IF LOAD # // User specified DECIMAL numbers - see Documentation below

# Interval 0 1 2 3 4 5 6 7 8 9 10 11

# Note C C# D D# E F F# G G# A A# B

# //—- Tuning A

ASS J00 = F12 F04 F12 F04 F12 F12 F04 F12 F04 F12 F03 F12 # 1. note

ASS J10 = D07 D06 D07 D06 D07 D07 D06 D10 D06 D07 D06 D06 # 2. note

ASS J20 = D16 D15 D15 D15 D15 D16 D15 D16 D15 D15 D15 D15 # 3. note

ASS J30 = D16 D15 D15 D15 D15 D16 D15 D16 D15 D15 D15 D15 # 4. note

# //—- Tuning B

ASS J40 = F12 F04 F12 F04 F12 F12 F04 F12 F04 F12 F03 F12 # 1. note

ASS J50 = D07 D06 D07 D06 D07 D07 D06 D10 D06 D07 D06 D06 # 2. note

ASS J60 = D16 D15 D15 D15 D15 D16 D15 D16 D15 D15 D15 D15 # 3. note

ASS J70 = D16 D15 D15 D15 D15 D16 D15 D16 D15 D15 D15 D15 # 4. note

# //—- Tuning C

ASS J80 = D16 D15 D15 D15 D15 D16 D15 D16 D15 D15 D15 D15 # 1. note

ASS J90 = D07 D06 D07 D06 D07 D07 D06 D10 D06 D07 D06 D06 # 2. note

ASS JA0 = F12 F04 F12 F04 F12 F12 F04 F12 F04 F12 F03 F12 # 3. note

ASS JB0 = F12 F04 F12 F04 F12 F12 F04 F12 F04 F12 F03 F12 # 4. note

# //—- Tuning D

ASS JC0 = F12 F04 F12 F04 F12 F12 F04 F12 F04 F12 F03 F12 # 1. note

ASS JD0 = D07 D06 D07 D06 D07 D07 D06 D10 D06 D07 D06 D06 # 2. note

ASS JE0 = D16 D15 D15 D15 D15 D16 D15 D16 D15 D15 D15 D15 # 3. note

ASS JF0 = D12 D12 D12 D12 D12 D12 D12 D12 D12 D12 D12 D12 # 4. note

# A B C D //— Strumming

ASS K00 = D00 D16 D09 D08 # Use D00 for ‚no-strumming‘, D16 for

# 1/16th strum, D12 for 1/16th triplets.

# //— BPM

ASS K04 = D110 # BPM needed for strumming

# //— Transpose

ASS K05 = D00 # chordOffset for shifting chord layout

ASS K06 = D00 # transpose for transposing output notes

# //— Humanise

ASS K07 = D00 # maxRandomDelay in +/- msec to humanize timing

# //— Tuning

ASS K10 = A # initialTuning (A,B,C or D)

END

# ============= Do not modify below this line ========================

# Acknowledgements

# First of all, big thanks to McDtracy - this script couldn‘t have

# been realized without his ideas and harmonics groundwork.

#

#

# Documentation

# The incomming note itself is suppressed, but used to generate

# up to 4 notes with user-specified offsets. The offset table

# contains these offsets for each of the 12 base notes.

#

# The script supports 4 tunings, selected by CC1 Modwheel

# input to the script. Each of the 4 tunings has independand

# strum setting. Use different order of note offsets to strum

# up,down or mixed.

#

# For humanization a random factor can be applied to jitter

# the note timings

#

#

# SPECIAL: Decimal user input

#

# To allow for decimal user input (instead of the default

# hexadecimal), a special notation is used for ALL numbers:

# Positive numbers start with D followed by the number

# Negative numbers start with F followed by the number

# For example +12 => D12, -36 => F36

#

#

# No double notes are spawned, so to generate a 3 note chord,

# just repeat the interval of note 1, 2 or 3 as 4th note.

#

#

# Label output

# The special notation is also used to generate error messages

# in the lables outputting the problematic variable. A „??J 4A“

# message means that J4A is malformed, a „|K| 05“ message shows

# that K05 is out of its allowed range.

#

# During ‚normal‘ operation, the left label shows the current

# tuning (selected by CC1) and the right label shows the interval

#

#

# Version Info

# V01 Initial version and idea by McDtracy

# V02 Refactored with arrays by -ki

# V03 Added chordOffset, Modwheel CC tunings and 4th note

# V04 Added stumming delay per tuning

# V05 Using injection/sysex for delayed notes

# V06 Added code to ignore double input triggering

# V07 All user inputs in BCD coded decimals

# V08 Computation of strum times via bpm

# V09 Root notes also go through SysEx to allow down-strum

# V10 Added code to prevent double note output

# V11 Added +/- maxRandomDelay for humanization

#

#

# Variables info

# =============

# Array L[00..7F] keyInfo[] // interval | keySeqId per note

# Array L[80..FF] down[] // play state of outgoing notes

# Array I[00..1F] stateArray[] // used for BCD conversion

# Array K[00..03] strumDelay[] // after conversion from divs

9X XX 00 = 8X # // convert NoteOn with vel=0 to NoteOff

# // Handle modulation wheel CC1

# // ===========================

#

# If a modwheeel CC is received, select the current tuning used

# during NoteOn

#

IF MT == B0 # IF (inputType == ControlerChange) {

IF M1 == 1 # IF (inputController == Modwheel) {

MAT IC0 = M2 / 20 # tuningIdx = inputValue / 20

MAT IC1 = IC0 * 40 # tuningOffset = tuningIdx * 40

MAT IA0 = IC0 + A # tmp = controllerIdx + A

SET LB0 IA0 # show(left, tmp)

XX = XX +B # // block current CC command

END # }

END # }

# // Handle NoteOn command

# // =====================

#

# If the input note is not active, inject delayed sysex for 4 chord

# notes depending on interval and „tuning“. Each keypress gets an

# SeqId to distinguish repeated keyprss of the same note.

# The seqId and current tuningOffset is stored for each key to

# allow the delayed note to identify if the keypress that spawned

# them is still active.

#

# The initial note itself is blocked, the sysex is picked up later

# and will decide if the spawnd notes start playing

#

IF MT == 90 # IF (inputType == NoteOn) {

ASS I81 = LM1 # info = keyInfo[inputNote]

MAT I82 = I81 & FF # infoInterval = info & 00FF

IF I82 == FF # IF (infoInterval == FREE) {

# // Compute new interval & seqId

MAT IA0 = M1 - K05 # transposed = inputNote - chordOffset

MAT K21 = IA0 % C # interval = transposed % 12

MAT I84 = K21 + IC1 # intervalOffset = interval + tuningOffset

MAT I81 = I81 + 100 # info += $0100 // inc infoSeqId

MAT I81 = I81 & 7F00 # info &= $7F00 // mask infoSeqId

MAT I81 = I81 | I84 # info |= interntervalOffset // add interval

ASS LM1 = I81 # keyInfo[inputNote] = info

MAT I83 = I81 / 100 # infoSeqId = info >> 8 // updated seqId

ASS IA2 = 0 # i = 0

ASS IA3 = 0 # currDelay = 0

ASS IA4 = I84 # currOffset = intervallOffset

IF IA2 < 4 +L # WHILE( i < 4 ) {

MAT IA5 = M1 + JIA4 # note = inputNote + offset[intervalOffset]

MAT IA5 = IA5 + K06 #

IF IA5 <= 7F # IF (note <= 127) {

ASS IA6 = IA3 # injectDelay = currDelay

IF K07 != 0 # IF (maxRandomDelay) {

#

ASS IA7 = R100 # r = random(256) // low part

# // R is interpreted as 0..1 and

# // multiplied with maxRandomDelay

# // using fixedpoint math. Negative

# // maxRandomDelay are supported

MAT IA8 = K07 & FF # dL = maxRandomDelay & FF

MAT IA9 = K07 / 100 # dH = maxRandomDelay / 256

IF IA9 > 80 # IF (dH > 127) {

MAT IA9 = FF00 | IA9 # dH = FF00 | dH // fix sign bits

END # }

MAT IAA = IA8 * IA7 # mL = dL * r // mul low part

MAT IAB = IA9 * IA7 # mH = dH * r // mul high part

MAT IAC = IAA / 100 # mL8 >>= 8 // shift low res

MAT IAD = IAB + IAC # rand = mH + mL8

MAT IA6 = IA6 + IAD # injectDelay += rand

IF IA6 < 0 # IF (injectDelay < 0) {

ASS IA6 = 0 # injectDelay = 0

END # }

END # }

# // Inject a custom delayed sysex with

# // basenote, infoSeqId,

# // inputCmd, note, inputVel

SND F0 7D M1 I83 M0 IA5 M2 F7 +I +DIA6 # // inject(sysex)

END # }

MAT IA2 = IA2 + 1 # i = i + 1

MAT IA3 = IA3 + KIC0 # currDelay += delay[tuningIdx]

MAT IA4 = IA4 + 10 # currOffset += $10 // Offset to next note

END # }

END # }

9X = XX +B # // block the received NoteOn

END # }

# // Handle NoteOff command

# // ======================

#

# If the note is active, mark it as FREE so that incomming delayed

# sysex notes know that they are invalid

# Also immediately send out NoteOffs for all chord notes of the

# tuning used at the NoteOn that spawned them.

#

# The incomming note-off itself is blocked.

#

IF MT == 80 # IF (inputType == NoteOff) {

ASS I81 = LM1 # info = keyInfo[inputNote]

MAT I82 = I81 & FF # infoInterval = info & 00FF

IF I82 != FF # IF (infoInterval != FREE) {

ASS I84 = I82 # intervalOffset = infoInterval

MAT IA1 = I81 & 7F00 # infoNew = info & FF00 // keep old infoSeqId

MAT IA1 = IA1 | FF # infoNew |= FREE // set interval FREE

ASS LM1 = IA1 # keyInfo[inputNote] = infoNew

ASS IA2 = 0 # i = 0

ASS IA4 = I84 # currOffset = intervallOffset

IF IA2 < 4 +L # WHILE( i < 4 ) {

MAT IA5 = M1 + JIA4 # note = inputNote + offset[intervalOffset]

MAT IA5 = IA5 + K06 #

IF IA5 <= 7F # IF (note <= 127) {

# // Immediately send NoteOffs if playing

MAT IA6 = IA5 + 80 # noteIdx = note + 128

IF LIA6 != 0 # IF (down[noteIdx] ) {

SND M0 IA5 M2 # send(inputCmd, note, inputVel)

ASS LIA6 = 0 # down[noteIdx] = 0

MAT K20 = K20 - 1 # playingNotesCnt -= 1

IF K20 == 0 # IF (playingNotesCnt == 0) {

SET LB0 S— # show(left, -)

SET LB1 S— # show(right,-)

END # }

END # }

END # }

MAT IA2 = IA2 + 1 # i = i + 1

MAT IA4 = IA4 + 10 # currOffset += $10 // Offset of next note

END # }

END # }

8X = XX +B # // block the received NoteOff

END # }

# // Handle SysEx injected chord notes

# // =================================

#

# All playing chord notes are injected via sysex and arrive

# possibly delayed for stumming. A note is only send to the

# output if the initial key is still pressed and still has the

# same seqId.

# It is also checkd that the note to be output is not already

# playing to prvent double note output.

#

IF M0 == F0 7D # IF (inputType == Sysex) {

MAT IA0 = LM2 & FF # sysExInterval = keyInfo[inputBaseNote] & 00FF

MAT IA1 = LM2 & 7F00 # sysExSeqId = keyInfo[inputBaseNote] & FF00

MAT IA1 = IA1 / 100 # sysExSeqId >>= 16

IF IA0 != FF # IF (sysExInterval != UNUSED) {

IF M3 == IA1 # IF (inputSeqId == sysExSeqId) {

# // base note still active and same id

MAT IA2 = M5 + 80 # noteIdx = inputNote + 128

IF LIA2 == 0 # IF (down[noteIdx] == 0) {

# // play note if not yet playing

SND M4 M5 M6 # send(inputCmd, inputNote, inputVelocity)

ASS LIA2 = 1 # down[noteIdx] = 1

MAT K20 = K20 + 1 # playingNotesCnt += 1

END # }

END # }

END # }

IF K20 > 0 # IF (playingNotesCnt > 0) {

MAT IA1 = IC0 + A # tmp = controllerIdx + A

SET LB0 IA1 # show(left, tmp)

SET LB1 K21 +D # show(right, intervall)

END # }

F0 7D = XX +B # // block received sysex

END # }

# // Initialisation

# // ===============================

#

# During initialisation all user input values in the arrays J

# and K are converted in-place from the special BCD format into

# a corresponding hex value. The code needed is quite lengthy.

# And because over 256 values need to be converted and StreamByter

# loops only allow max 128 iterations a ‚subroutine‘ is repeated

# three times.

#

# The BCD conversion ‚subroutine‘ itself iterates over the 4 digits

# and changes behavior after the initial D or F is found. The code

# is unrolled (each nested loop eats up max iteration count) and the

# logic put into a state maschine using either stateArray[0-F] for

# finding the D or F, or stateArray[10-1F] for processing a number.

#

# After the lengthy conversion, the BPM is computed into a msec per

# bar value, which is then used to compute the stum-delays from the

# strum note divisions entered by the user.

#

# BPM conversion had to solve the problem that the number 240000

# needed for the computation is larger than the numbers supported

# by streambyter. This is done by splitting the computation into

# an upper and lower part.

#

#

IF LOAD # // Run only once

# // Prepare keyInfo[]

# // ————————————————-

ASS IA0 = 0 # i = 0

IF IA0 < 80 +L # WHILE( i< 128 ) {

ASS LIA0 = FF # keyInfo[i] = FREE

MAT IA1 = IA0 + 80 # noteIdx = i + 128

ASS LIA1 = 0 # down[noteIdx] = 0

MAT IA0 = IA0 + 1 # i += 1

END # }

ASS K20 = 0 # playingNotesCnt = 0

# // Prepare stateArray[] for BCD conversion

# // ——————————————————————————————————————-

ASS IA0 = 0 # i = 0

IF IA0 < 20 +L # WHILE( i< 32 ) {

ASS IIA0 = 0010 # stateArray[i] = OKAY | PARSE

IF IA0 < 10 # IF (i<16) {

ASS IIA0 = 1000 # stateArray[i] = ERROR | CHECK

END # }

IF IA0 > 19 # IF (i>23) {

ASS IIA0 = 1010 # stateArray[i] = ERROR | PARSE

END # }

MAT IA0 = IA0 + 1 # i += 1

END # }

ASS I00 = 0000 # stateArray[0] = OKAY | CHECK

ASS I0D = 0110 # stateArray[D] = POS | PARSE

ASS I0F = 0210 # stateArray[F] = NEG | PARSE

# // In-place convert BCD-coded offset[] array

# // —————————————————————————————————————————-

ASS IB8 = 0 # loopIdx = 0

IF IB8 < 80 +L # WHILE( loopIdx < 128) {

ASS IDD = JIB8 # bcdValue = offset[loopIdx]

# | SUBROUTINE Unrolled BCD to hex code

# | ==============================

# | // Input in var IDD, output hex in var ID0

# | // Uses vars ID0-IDD and stateArray I00-I1F

# |

ASS ID0 = 0 # | outValue = 0

# |

# | // First digit, either 0,D or F

MAT ID1 = IDD & F000 # | digit = bcdValue & byte4mask

MAT ID1 = ID1 / 1000 # | digit = digit >> byte4shift

ASS ID5 = IID1 # | state = stateArray[digit]

# |

# | // Second digit, either 0-9 or D,F

MAT ID1 = IDD & 0F00 # | digit = bcdValue & byte3mask

MAT ID1 = ID1 / 0100 # | digit = digit >> byte3shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# | // Third digit, either 0-9 or D,F

MAT ID1 = IDD & 00F0 # | digit = bcdValue & byte2mask

MAT ID1 = ID1 / 0010 # | digit = digit >> byte2shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# |// Forth digit, either 0-9 or D,F

MAT ID1 = IDD & 000F # | digit = bcdValue & byte3mask

MAT ID1 = ID1 / 0001 # | digit = digit >> byte3shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# // conversion result handling

# // ..........................

ASS JIB8 = 0 # offset[loopIdx] = 0 // default value

IF ID5 >= 1000 # IF (state >= ERROR) {

SET LB0 S??J # show(left, ‘??J‘)

SET LB1 IB8 # show(right, loopIdx)

ASS IB8 = 100 # loopIdx = 256 // terminate loop

END # }

IF ID5 < 1000 # IF (state < ERROR) {

IF ID0 > 7F # IF (outValue > 127 ) {

SET LB0 S|J| # show(left, ‘|J|‘)

SET LB1 IB8 # show(right,loopIdx)

ASS IB8 = 100 # loopIdx = 256 // terminate loop

END # }

IF ID0 < 7F # IF (outValue < 128) {

IF ID3 == 0200 # | IF (stateType == NEG) {

MAT ID0 = 0 - ID0 # | outValue = -outValue

END # | }

ASS JIB8 = ID0 # offset[loopIdx] = outValue

END # }

END # }

# // Since loops only run 128 time but we

# // have 256 entries, we need separate code

# // for lower and upper half

MAT IB9 = IB8 + 80 # loopIdx2 = loopIdx + 128

ASS IDD = JIB9 # bcdValue = offset[loopIdx2]

# | SUBROUTINE Unrolled BCD to hex code

# | ==============================

# | // Input in var IDD, output hex in var ID0

# | // Uses vars ID0-IDD and stateArray I00-I1F

# |

ASS ID0 = 0 # | outValue = 0

# |

# | // First digit, either 0,D or F

MAT ID1 = IDD & F000 # | digit = bcdValue & byte4mask

MAT ID1 = ID1 / 1000 # | digit = digit >> byte4shift

ASS ID5 = IID1 # | state = stateArray[digit]

# |

# | // Second digit, either 0-9 or D,F

MAT ID1 = IDD & 0F00 # | digit = bcdValue & byte3mask

MAT ID1 = ID1 / 0100 # | digit = digit >> byte3shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# | // Third digit, either 0-9 or D,F

MAT ID1 = IDD & 00F0 # | digit = bcdValue & byte2mask

MAT ID1 = ID1 / 0010 # | digit = digit >> byte2shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# |// Forth digit, either 0-9 or D,F

MAT ID1 = IDD & 000F # | digit = bcdValue & byte3mask

MAT ID1 = ID1 / 0001 # | digit = digit >> byte3shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# // conversion result handling

# // ..........................

ASS JIB9 = 0 # offset[loopIdx2] = 0 // default value

IF ID5 >= 1000 # IF (state >= ERROR) {

SET LB0 S??J # show(left, ‘??J‘)

SET LB1 IB9 # show(right, loopIdx2)

ASS IB8 = 100 # loopIdx = 256 // terminate loop

END # }

IF ID5 < 1000 # IF (state < ERROR) {

IF ID0 > 7F # IF (outValue > 127 ) {

SET LB0 S|J| # show(left, ‘|J|‘)

SET LB1 IB9 # show(right,loopIdx2)

ASS IB8 = 100 # loopIdx = 256 // terminate loop

END # }

IF ID0 < 7F # IF (outValue < 128) {

IF ID3 == 0200 # | IF (stateType == NEG) {

MAT ID0 = 0 - ID0 # | outValue = -outValue

END # | }

ASS JIB9 = ID0 # offset[loopIdx2] = outValue

END # }

END # }

MAT IB8 = IB8 + 1 # loopIdx += 1

END # }

ASS K08 = 7F 7F 7F 7F 12C C FFFF FFFF # rangeK[] for bcd conversion

# // In-place BCD conversion of arrayK[] to hex

# // —————————————————————————————————————————-

ASS IB8 = 0 # loopIdx = 0

IF IB8 < 8 +L # WHILE( loopIdx < 4) {

ASS IDD = KIB8 # bcdValue = arrayK[loopIdx]

# | SUBROUTINE Unrolled BCD to hex code

# | ==============================

# | // Input in var IDD, output hex in var ID0

# | // Uses vars ID0-IDD and stateArray I00-I1F

# |

ASS ID0 = 0 # | outValue = 0

# |

# | // First digit, either 0,D or F

MAT ID1 = IDD & F000 # | digit = bcdValue & byte4mask

MAT ID1 = ID1 / 1000 # | digit = digit >> byte4shift

ASS ID5 = IID1 # | state = stateArray[digit]

# |

# | // Second digit, either 0-9 or D,F

MAT ID1 = IDD & 0F00 # | digit = bcdValue & byte3mask

MAT ID1 = ID1 / 0100 # | digit = digit >> byte3shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# | // Third digit, either 0-9 or D,F

MAT ID1 = IDD & 00F0 # | digit = bcdValue & byte2mask

MAT ID1 = ID1 / 0010 # | digit = digit >> byte2shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

# |// Forth digit, either 0-9 or D,F

MAT ID1 = IDD & 000F # | digit = bcdValue & byte3mask

MAT ID1 = ID1 / 0001 # | digit = digit >> byte3shift

MAT ID2 = ID5 & 00FF # | stateOffset = state & offsetMask

MAT ID3 = ID5 & 0300 # | stateType = state & typeMask

MAT ID4 = ID1 + ID2 # | digitOffset = digit + stateOffset

MAT ID5 = ID5 | IID4 # | state = state | stateArray[digitOffset]

IF ID3 > 0 # | IF (stateType > 0) {

MAT ID0 = ID0 * A # | outValue *= 10

MAT ID0 = ID0 + ID1 # | outValue += digit

END # | }

# |

IF ID3 == 0200 # | IF (stateType == NEG) {

MAT ID0 = 0 - ID0 # | outValue = -outValue

END # | }

# // conversion result handling

# // ..........................

ASS KIB8 = 0 # arrayK[loopIdx] = 0 // default value

IF ID5 >= 1000 # IF (state >= ERROR) {

SET LB0 S??K # show(left, ‘??K‘)

SET LB1 IB8 # show(right,loopIdx)

ASS IB8 = 100 # loopIdx = 256 // terminate loop

END # }

IF ID5 < 1000 # IF (state < ERROR) {

MAT IB9 = IB8 + 8 # loopIdx2 = loopIdx + 8

IF ID0 > KIB9 # IF (outValue > rangeK[loopIdx2] ) {

SET LB0 S|K| # show(left, ‘|K|‘)

SET LB1 IB8 # show(right,‘loopIdx)

ASS IB8 = 100 # loopIdx = 256 // terminate loop

END # }

IF ID0 <= KIB9 # IF (outValue <= rangeK[loopIdx2]) {

ASS KIB8 = ID0 # stumDiv[loopIdx] = outValue

END # }

END # }

MAT IB8 = IB8 + 1 # loopIdx += 1

END # }

# // Compute milliseconds per bar from bpm

# // —————————————————————————————————————

# // msec = 240000 / bpm

# // but 240000 is out of StreamByter number range

#

MAT ID0 = K04 * 10 # bpm16 = bpm * 16

MAT ID1 = 3A98 / ID0 # msecHigh = (240000/16) / bpm16

MAT ID2 = ID1 * K04 # partTmp = msecHigh * bpm

MAT ID3 = ID2 * 10 # partHigh = partTmp * 16

MAT ID4 = 3A98 - ID3 # diff = 15000 - partHigh

MAT ID5 = ID4 * 10 # diff16 = diff * 16

MAT ID6 = ID5 / K04 # msecLow = diff16 / bpm

MAT ID7 = ID1 * 100 # msecTmp = msecHigh * 256

MAT K91 = ID7 + ID6 # msec = msecTmp + msecLow

# // Calculate strum delays from strumDiv[]

# // ——————————————————————————————————————

# // using msec

ASS IB8 = 0 # loopIdx = 128

IF IB8 < 4 +L # WHILE( loopIdx < 132) {

ASS ID0 = KIB8 # div = strumDiv[loopIdx]

IF ID0 != 0 # IF (div != 0) {

MAT ID1 = ID1 / 2 # div2 = div/2

MAT ID2 = K91 + ID1 # msecR = msec + div2 // Rounding

MAT ID3 = ID2 / ID0 # delay = msecR / div

ASS KIB8 = ID3 # strumDelay = delay

END # }

MAT IB8 = IB8 + 1 # loopIdx += 1

END # }

# // Select initial tuning

# // ————————————————————-

ASS ID0 = 0 # error = 0

IF K10 < A # IF (initialTuning < ‚A‘) {

ASS ID0 = 1 # error = 1

END # }

IF K10 > D # IF (initialTuning > ‚D‘) {

ASS ID0 = 1 # error = 1

END # }

IF ID0 == 0 # IF ( !error ) {

MAT IC0 = K10 - A # tuningIdx = initialTuning - 10

MAT IC1 = IC0 * 40 # tuningOffset = tuningIdx * 40

END # }

IF ID0 != 0 # IF ( error ) {

SET LB0 S|K| # show(left, „|K|“)

SET LB1 S10 # show(right,“10“ )

END # }

END #