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
* 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
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 #