Chris Kelly
Well-known member
Hi guys
I've hit a speed wall in my program. I purchased a 40X2 with 16MHz resonator in the hope to include some extra code/features with an ongoing circular buffer program I've been working on to drive a home-made drum machine at the desired speed of 120bpm.
A quick summary of the program's aim is to work through a buffer of length 128, driving each bPtr increment with an external clock. 120bpm on a 16-step sequence, actually works out at 64 increments per second, since each step on the drum machine is sub-divided into 8 increments.
For 120bpm on a standard drum pattern, each 'beat' is usually every 4 steps:
X - - - X - - - X - - - X - - -
|<- 1 ->|<- 2 ->|<- 3 ->|<- 4 ->|
My original code was too sluggish, so I removed nested If..then statements involving the same variables where possible, but still too slow. I've since stripped down most of the features but the fastest I can achieve is about 60bpm.
If anyone can spot any statements in my code that can be optimised to do the same job but quicker, I'd really appreciate feedback. I know it might be asking a lot to speed things up even further, because the do …. loop section of code in essence needs to complete in 15ms or less.
If it's not feasible, then I might just admit defeat and settle for the slower program. (I could even think of it as a buffer of length 64 running at 120bpm!)
Thanks again guys and apologies if I've broken forum etiquette by posting so much code
Chris
I've hit a speed wall in my program. I purchased a 40X2 with 16MHz resonator in the hope to include some extra code/features with an ongoing circular buffer program I've been working on to drive a home-made drum machine at the desired speed of 120bpm.
A quick summary of the program's aim is to work through a buffer of length 128, driving each bPtr increment with an external clock. 120bpm on a 16-step sequence, actually works out at 64 increments per second, since each step on the drum machine is sub-divided into 8 increments.
For 120bpm on a standard drum pattern, each 'beat' is usually every 4 steps:
X - - - X - - - X - - - X - - -
|<- 1 ->|<- 2 ->|<- 3 ->|<- 4 ->|
My original code was too sluggish, so I removed nested If..then statements involving the same variables where possible, but still too slow. I've since stripped down most of the features but the fastest I can achieve is about 60bpm.
If anyone can spot any statements in my code that can be optimised to do the same job but quicker, I'd really appreciate feedback. I know it might be asking a lot to speed things up even further, because the do …. loop section of code in essence needs to complete in 15ms or less.
Code:
#picaxe 40X2
#no_data
'==== LOOPER CIRCUIT.
'A sequence can be from 1-16 "steps" in length.
'Data is input at either C.2 ("hard") or C.3 ("soft")
'and will output at either B.0 or B.1 when bPtr
'detects data at the current address.
'C.2 alone will input data. C.3 only adds data if
'data from C.2 is already at the address, or
'if C.2 and C.3 add data simultaneously
'Input at C.1 will overwrite or wipe any existing
'data at that address, allowing patterns of data
'to be overwritten in real-time
'An external clock input at C.0 drives the program,
'and 8 clock pulses will constitute one whole "step"
'of the 16 step pattern.
'i.e/ a full pattern is 128 pulses in total
'An ADC input at C.7 cause a subroutine to change
'the total buffer length, in multiples of 8 clock
'pulses. i.e/ the ADC determines how many "steps"
'are in the sequence.
'Two buffer start addresses allow for two separate
'banks of data to be accessed.
'A sequence can reset at the end of a full "step"
'and will revert to the buffer start address
symbol HighOutPin = B.0 ' High Output
symbol LowOutPin = B.1 ' Low Output
symbol BankOutPin = B.2 ' Bank Swap Indicator
symbol ClkPin = pinC.0 ' Clock Input
symbol OverWritePin = pinC.1 ' Overwrite data
symbol HardPin = pinC.2 ' Data Input (Hard)
symbol SoftPin = pinC.3 ' Data Input (Soft)
symbol RstPin = pinC.4 ' Reset Loop
symbol BankPin = pinC.5 ' Bank Swap pin
symbol CopyPin = pinC.6 ' Copy Bank to other
symbol SeqLength = b4 ' Sequence Length Bank 1
symbol SeqLength2 = b5 ' Sequence Length Bank 2
symbol BufLen = b6 ' Buffer Length Bank 1
symbol BufEnd = b7 ' Address of buffer end 1
symbol Active = b8 ' Shows data on that step
symbol ClockCount = b9 ' Count 1-8 clock pulses
symbol HardPin1Shot = b10 ' Add data (one-shot)
symbol RstPin1Shot = b11 ' Add Reset (one-shot)
symbol BankPin1Shot = b12 ' Add BankSwap(one-shot)
symbol BankSwap = b13 ' For new bptr start posn
symbol ActiveBank = b14 ' Indicates current bank
symbol ADC1 = b15 ' Detect ADC change
symbol ADC2 = b16
symbol BufEnd2 = w14 ' Address of buffer end 2
symbol MyPtr = w15
symbol ClkPinCopy = bit0
symbol ClkPinmem = bit1
symbol ClkOneShot = bit2 ' On Clock rising edge
symbol HardPinCopy = bit8
symbol HardPinMem = bit9
symbol HardOneShot = bit10 ' On Data rising edge
symbol RstPinCopy = bit16
symbol RstPinMem = bit17
symbol RstOneShot = bit18 ' On Reset rising edge
symbol BankPinCopy = bit24
symbol BankPinMem = bit25
symbol BankOneShot = bit26 ' On Bankswap rising edge
symbol BufStart = 32 ' Buf Start address Bank 1
symbol BufStart2 = 160 ' Buf Start address Bank 2
'==== MAIN PROGRAM STARTS ====
'=============================
gosub ReadSeqLength
bptr = BufStart
'==== The following do...loop takes place during each
'clock cycle =============================
do
main:
readadc C.7,b15
if ADC1 != ADC2 then
gosub ReadSeqLength
end if
MyPtr = bptr
'==== ONE-SHOT DECLARATIONS ====
ClkPinCopy = ClkPin ' Advance clock on rising edge
ClkOneShot = ClkPinCopy &/ ClkPinMem
ClkPinMem = ClkPinCopy
HardPinCopy = HardPin ' Add Data on rising edge
HardOneShot = HardPinCopy &/ HardPinMem
HardPinMem = HardPinCopy
RstPinCopy = RstPin ' Add Reset on rising edge
RstOneShot = RstPinCopy &/ RstPinMem
RstPinMem = RstPinCopy
BankPinCopy = BankPin ' Add Reset on rising edge
BankOneShot = BankPinCopy &/ BankPinMem
BankPinMem = BankPinCopy
'==== SET DATA ONE-SHOT BETWEEN CLOCK PULSES ====
'============
if OverWritePin = 1 then
@bptr = 0
end if
if HardOneShot = 1 then
HardPin1Shot = 1
end if
if RstOneShot = 1 then
RstPin1Shot = 1
end if
if BankOneShot = 1 then
BankPin1Shot = 1
BankSwap = 1
end if
'==== DETECT RISING EDGE OF CLOCK PULSE AT C.1 ====
'==============
if ClkOneShot = 1 then
'==== the following code triggers a flashing indicator for when the
'current data bank will be swapped
'==========
if Bankswap = 1 then
select ClockCount
case 0
high BankOutPin
case 1
high BankOutPin
case 2
low BankOutPin
case 3
low BankOutPin
case 4
high BankOutPin
case 5
high BankOutPin
case 6
low BankOutPin
case 7
low BankOutPin
end select
end if
' ==== Detect data entry
' Adds a 1 to buffer position for High hit
' Adds a 2 to buffer position for Soft hit
'==========
if HardPin1Shot = 1 then
Select SoftPin
case 0
@bptr = HardPin1Shot
case 1
@bptr = HardPin1Shot + 1
end select
end if
if @bptr = 1 and SoftPin = 1 then
@bptr = 2
end if
'==== Output at one of the pins to trigger either
'a hard hit (B.0) or soft hit (B.1)
'====
Active = @bptr
select Active
case 0
low HighOutPin ' No Output
low LowOutPin
case 1
high HighOutPin ' Output data at B.0
low LowOutPin
case 2
high LowOutPin ' Output data at B.1
low HighOutPin
end select
'==== Increment buffer.=====
'Program keeps count of each clock pulse. Since each
'step of the pattern requires 8 clock pulses, the
'code tracks the "Clock Count" and only executes
'certain features at the start of each step
' i.e/ when ClockCount = 0
'=========
inc bptr
inc ClockCount
if ClockCount > 7 then
ClockCount = 0
end if
if ClockCount = 0 then
'======
' when reset pin pressed, the buffer returns to the start
'once the current "step" has completed. If Bankswap is required
'then the buffer jumps to the second bank of data
'======
if RstPin1shot = 1 and ActiveBank = 0 then
select Bankswap
case 0
bptr = BufStart
RstPin1Shot = 0
case 1
bptr = BufStart2
BankSwap = 0
ActiveBank = 1
RstPin1Shot = 0
end select
end if
if RstPin1shot = 1 and ActiveBank = 1 then
select Bankswap
case 0
bptr = BufStart2
RstPin1Shot = 0
case 1
bptr = BufStart
BankSwap = 0
ActiveBank = 0
RstPin1Shot = 0
end select
end if
'========================================
'when the end of the buffer is reached, bPtr returns to
'the start of the buffer once the current "step" has completed.
'If Bankswap is required, then the buffer jumps to the second bank of data
'================================================
if bptr > BufEnd and ActiveBank = 0 then
select BankSwap
case 0
BufEnd = BufStart + BufLen - 1
bptr = BufStart
case 1
BufEnd2 = BufStart + BufLen + 127
bptr = BufStart2
BankSwap = 0
ActiveBank = 1
end select
end if
if bptr > BufEnd2 and ActiveBank = 1 then
select BankSwap
case 1
BufEnd2 = BufStart + BufLen + 127
bptr = BufStart2
case 0
BufEnd = BufStart + BufLen - 1
bptr = BufStart
BankSwap = 0
ActiveBank = 0
end select
end if
end if
'Any current one-shot variables return to 0
'=================================
HardPin1Shot = 0
BankPin1Shot = 0
endif
loop
'Sub routine below is called if ADC value changes
'=================================
ReadSeqLength:
readadc C.7,b4
select SeqLength
case <16
BufLen = 8
case 16 to 31
BufLen = 16
case 32 to 47
BufLen = 24
case 48 to 63
BufLen = 32
case 64 to 79
BufLen = 40
case 80 to 95
BufLen = 48
case 96 to 111
BufLen = 56
case 112 to 127
BufLen = 64
case 128 to 143
BufLen = 72
case 144 to 159
BufLen = 80
case 160 to 175
BufLen = 88
case 176 to 191
BufLen = 96
case 192 to 207
BufLen = 104
case 208 to 223
BufLen = 112
case 224 to 239
BufLen = 120
case 240 to 256
BufLen = 128
end select
BufEnd = BufStart + BufLen - 1
BufEnd2 = BufStart + BufLen + 127
ADC2 = ADC1
Return
Thanks again guys and apologies if I've broken forum etiquette by posting so much code
Chris