tap tempo snippet

Algorithm development and general DSP issues

Moderator: frank

slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

tap tempo snippet

Post by slacker »

If anyone's interested here's some taptempo code I came up with. I tested it out by hacking it into some existing delay code and it works. It measures the time between two consecutive taps and outputs a value between 0 and 1 proportional to the time between them up to a max of 1 second. I need to add a timeout if the time between taps is too long.

The code up to "ENDSWITCH" can be used on its own to create a momentary and latching switch from a tap input.

Hopefully the comments explain how it works, if not just ask. If anyone's got any ideas for improvements, or alternative ways of doing it feel free to post them.

Code: Select all

;tap tempo
;pot 2 tap input
;output is a value between 0 and 1 written to taptempo register,
;corresponding to a delay of 0 to 1000ms if using the full delay memory

equ  db reg0		        ;debounce
equ  mom reg1		;momentary output of switch +1 high, -1 Low
equ  latch reg2		;latched output of switch +1 high, -1 low
equ ramp reg3		        ;current value of rmpo, scaled to 0 to 1
equ taptempo reg4	        ;taptempo value, 0 to 1

equ count 0.01		;debounce counter

skp run,START
wldr rmp0,0.064,4096	;set up rmp0
sof 0,0.999		
wrax latch,0		        ;set latch = 1 high

START:

;Switch Debouncing and pot filtering work around

ldax   pot2	          ;read pot2
sof 1,-0.5	                  ;level shift to -0.5 to 0.5
skp neg,DOWN	  ;if negative jump to DOWN
ldax db		          ;else high, read db
sof 1,count	          ;add count
wrax db,0	          ;write new value to db
skp zro,ENDDB	  ;jump to ENDDB
DOWN:
ldax db		           ;read db
sof 1,-count               ;deduct count
wrax db,0	          ;write new value to db

ENDDB:

;latching switch, falling edge triggered flipflop
;Output of debounce routine of < -0.9 is low, > 0.9 is high, values in between
;are ignored and the switch does nothing, Schmitt trigger action.


ldax db			         ;read db
absa			         ;get absolute value
sof 1,-0.9		                 ;deduct 0.9 so only values < -0.9 or > 0.9 give a positive result
skp neg,ENDSWITCH	;if negative then jump to ENDSWITCH
ldax db			        ;read db
sof 1,-0.9		                ;deduct 0.9
skp neg,LO		        ;if negative jump to LO, output of debounce is low 
sof 0,0.999	            	;else output of debounce is high
wrax mom,0		        ;set mom to 1 (high)
skp zro,ENDSWITCH	;jump to ENDSWITCH
LO:
ldax mom		                ;read mom
skp neg,ENDSWITCH	;if it's negative then debounce was already low last time so do nothing, jump to ENDSWITCH
sof 0,-0.999		         ;else mom was high last time so switch has only just been pressed (falling edge)
wrax mom,0		         ;set mom to -1 (low)
ldax latch		                ;read latch
sof -1,0			         ;invert, high becomes low, low becomes high
wrax latch,0		           ;write to value to latch

ENDSWITCH:

;tap tempo, uses rmp0 as a 1 Hz rising ramp, runs whilst latch is low and is sampled and held when latch is high

ldax latch		             ;read latch
skp neg,LOW		     ;if negative jump to LOW
jam rmp0		              ;else latch is high, jam rmp0 (reset to 0)
ldax ramp		      ;read ramp, will contain last value of rmp0 before latch went high	
wrax taptempo,0		;write to taptempo
skp zro,ENDTT		;jump to ENDTT
LOW:
sof 0,0.064		
wrax rmp0_rate,1		;set rmp0 rate to 1Hz
cho rdal,rmp0		         ;read value of rmp0
sof -2,0.999		          ;level shift to 0 to 1 rising ramp
wrax ramp,0		         ;write to ramp
ENDTT:

ldax taptempo
wrax dacl,0
Sweetalk
Posts: 141
Joined: Thu Oct 15, 2009 5:13 am

Post by Sweetalk »

Great!!! anyway easy to test it with a delay?.
slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

Post by slacker »

To add to exiting delay code change pot2 in this line to the pot that your delay code uses to set the delay time.

Code: Select all

ldax   pot2             ;read pot2 
Then add your delay code after

Code: Select all

ENDTT: 
where your delay code reads from a pot replace the pot name with taptempo so for example

Code: Select all

rdax pot2,1 
becomes

Code: Select all

rdax taptempo,1 
I tested it by hacking it into the simple delay here http://www.spinsemi.com/forum/viewtopic.php?t=124
Sweetalk
Posts: 141
Joined: Thu Oct 15, 2009 5:13 am

Post by Sweetalk »

Thank you for the super fast answer!!! I'll try it asap!!
Sweetalk
Posts: 141
Joined: Thu Oct 15, 2009 5:13 am

Post by Sweetalk »

One last question before I try it. In the taptempo code there are to lines in the end:

Code: Select all

ldax taptempo
wrax dacl,0
You said that I paste the delay code after the ENDTT: , so, I erase those two lines or where should I put them?
slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

Post by slacker »

You can delete those 2 lines, sorry for not explaining that earlier.
I've written a simple delay using the code, I'll post it sometime this week. I'm still working on making it reset if the time between taps is too long, I thought I had it figured out but it's not working right.
Sweetalk
Posts: 141
Joined: Thu Oct 15, 2009 5:13 am

Post by Sweetalk »

Great, i'm working on that too. Also thinking some way to have a flashing led on the tempo. Maybe a square wave lfo in the left out?.
slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

Post by slacker »

Here's an updated version of the code, including a simple delay. The taptempo code now times out if the time between taps is too long, it's still not working 100% correctly though, I need to start a new thread about the issues I'm having with it.
It also includes a tap tempo indicator on the right DAC, it flashes the clipping LED at the current tempo, but it could be modified to flash an external LED instead.

Code: Select all

;tap tempo delay
;pot 2 = tap input
;pot 0 = feedback
;pot 1 = delay level

equ  db reg0		;debounce
equ  mom reg1		;momentary output of switch +1 high, -1 Low
equ  latch reg2		;latched output of switch +1 high, -1 low
equ ramp reg3		;current value of rmpo, scaled to 0 to 1
equ taptempo reg4	;taptempo value, 0 to 1
equ fback reg5		;feedback
equ delayout reg6		;delay output
equ clip reg7		;clipping

equ count 0.01		;debounce counter


mem delay 32767

skp run,START
wldr rmp0,0.064,4096	;set up rmp0
wldr rmp1,0.064,4096	;set up rmp1
sof 0,0.999		
wrax latch,0		;set latch = 1 high

START:

;Switch Debouncing and pot filtering work around

ldax   pot2	;read pot2
sof 1,-0.5	;level shift to -0.5 to 0.5
skp neg,DOWN	;if negative jump to DOWN
ldax db		;else high, read db
sof 1,count	;add count
wrax db,0	;write new value to db
skp zro,ENDDB	;jump to ENDDB
DOWN:
ldax db		;read db
sof 1,-count	;deduct count
wrax db,0	;write new value to db

ENDDB:

;latching switch, falling edge triggered flipflop
;Output of debounce routine of < -0.9 is low, > 0.9 is high, values in between
;are ignored and the switch does nothing, Schmitt trigger action.


ldax db			;read db
absa			;get absolute value
sof 1,-0.9		;deduct 0.9 so only values < -0.9 or > 0.9 give a positive result
skp neg,ENDSWITCH	;if negative then jump to ENDSWITCH
ldax db			;read db
sof 1,-0.9		;deduct 0.9
skp neg,LO		;if negative jump to LO, output of debounce is low 
sof 0,0.999		;else output of debounce is high
wrax mom,0		;set mom to 1 (high)
skp zro,ENDSWITCH	;jump to ENDSWITCH
LO:
ldax mom		;read mom
skp neg,ENDSWITCH	;if it's negative then debounce was already low last time so do nothing, jump to ENDSWITCH
sof 0,-0.999		;else mom was high last time so switch has only just been pressed (falling edge)
wrax mom,0		;set mom to -1 (low)
ldax latch		;read latch
sof -1,0			;invert, high becomes low, low becomes high
wrax latch,0		;write to value to latch

ENDSWITCH:

;tap tempo, uses rmp0 as a 1 Hz rising ramp, runs whilst latch is low and is sampled and held when latch is high

ldax latch		;read latch
skp neg,LOW		;if negative jump to LOW
jam rmp0		;else latch is high, jam rmp0 (reset to 0)
ldax ramp		;read ramp, will contain last value of rmp0 before latch went high	
wrax taptempo,0		;write to taptempo
skp zro,ENDTT		;jump to ENDTT
LOW:
sof 0,0.064		
wrax rmp0_rate,1		;set rmp0 rate to 1Hz
cho rdal,rmp0		;read value of rmp0
sof -2,0.999
sof 1,0.001		;level shift to 0 to 1 rising ramp
wrax ramp,1		;write to ramp
sof 1,-0.85
skp neg,ENDTT
ldax taptempo
wrax ramp,0
sof 0,0.999
wrax latch,0
ENDTT:

;tap tempo indicator, flashes clipping LED at tap tempo

clr
wrax dacr,0
sof 0,0.064		
wrax rmp1_rate,1		;set rmp1 rate to 1Hz
cho rdal,rmp1		;read value of rmp1
sof -2,0.999
sof 1,0.001
rdax taptempo,-0.5
skp neg,ENDLED
jam rmp1
sof 0,0.999
wrax dacr,0
ENDLED:

ldax fback
rdax adcl,1
wra delay,0

ldax taptempo
wrax addr_ptr,0

rmpa 1
wrax delayout,1

mulx pot0
wrax clip,-0.33333
mulx clip
mulx clip
rdax clip,1

wrax fback,0

rdax delayout,1
mulx pot1
rdax adcl,1

wrax dacl,0
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

slacker wrote: I need to start a new thread about the issues I'm having with it.
I've been waiting for you to start the new thread to learn how things are going, please keep posting, I enjoy reading other people code and how they solved an issue.
Frank Thomson
Experimental Noize
slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

Post by slacker »

Sorry Frank, I was busy with other things and didn't get round to posting about the problems I was having. I got some time yesterday and figured out what the problem was, turns out it was a user error on my part. Here's the bit of code that wasn't working properly.

Code: Select all

sof 0,0.064       
wrax rmp0_rate,1      ;set rmp0 rate to 1Hz 
cho rdal,rmp0      ;read value of rmp0 
sof -2,0.999 
sof 1,0.001      ;level shift to 0 to 1 rising ramp 
wrax ramp,1      ;write to ramp 
sof 1,-0.998 
skp neg,ENDTT 
ldax taptempo 
wrax ramp,0 
sof 0,0.999 
wrax latch,0
The idea of this is to start a 1Hz ramp on the first press of the tap tempo switch, convert that from a value between 0 and 0.5 into a value to between 0 and 1, a second tap of the switch then grabs that value and writes it to a register that can be used to generate a taptempo control of a delay or whatever. That appeared to work, except that if a second tap wasn't received within 1 second, the ramp started again so the second bit of the code starting with the sof 1,-0.998 line was supposed to reset the system once the ramp reached 1 second. The problem was it didn't do anything, it was as though the ramp was never getting to 1.

The problem, if you haven't already spotted it, was with this line

wrax rmp0_rate,1 ;set rmp0 rate to 1Hz

This was writing 0.064 to the ramp rate, but then leaving it in the accumulator so I guess the following cho command was adding this to the ramp value. So by the time I'd done the sof -2,0.999 instead of getting values between 0 and 1 I was getting values between -0.129 and 0.871 so the ramp never got high enough the trigger the reset. Correcting the line to wrax rmp0_rate,0 solved the problem and it now works properly.
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

Yup, been there, left a coefficient at "1" instead of "0" and had it mess up the rest of the code. Glad to see you found it and got it solved, it is one of those problems you can look at 19 times and not see until the 20th review of the code and it's "D'OH!!! How could I miss that???"
Frank Thomson
Experimental Noize
slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

Post by slacker »

Just a quick follow up, here's the latest version of the code. This now sets the initial delay time to 333ms, the original started with no delay until one was tapped in. A square wave at the tap tempo rate is sent to DACR, with a suitable driver this will flash an LED.

Code: Select all

;tap tempo delay
;mono input (ADCL) mono output (DACL)
;a 0 - 0.99 square wave at the tap tempo rate is send to DACR. This can be used to flash a LED using a suitable driver. 
;Pot 2 is used as a tap tempo switch input. This should be a momentary switch, transition can be high to low or low to high.
;see guitar amp application note for examples of switch hookup.  
;pot 0 = feedback
;pot 1 = delay level

;set up registers and equates

equ  db reg0		;debounce
equ  mom reg1		;momentary output of switch +1 high, -1 Low
equ  latch reg2		;latched output of switch +1 high, -1 low
equ ramp reg3		;current value of rmpo, scaled to 0 to 1
equ taptempo reg4	;taptempo value, 0 to 1
equ fback reg5		;feedback
equ delayout reg6		;delay output
equ clip reg7		;clipping
equ led reg8		;taptempo LED

equ count 0.01		;debounce counter
equ delaytime 330		;initial delay time in milli seconds

mem delay 32767

skp run,START
wldr rmp0,0.064,4096	;set up rmp0
wldr rmp1,0.064,4096	;set up rmp1
sof 0,0.99		
wrax latch,1		;set latch = 1 high
wrax led,0		;set led = 1 high
sof 0,delaytime/1000	;set initial delay time
wrax ramp,0

START:

;Switch Debouncing and pot filtering work around

ldax   pot2	;read pot2
sof 1,-0.5	;level shift to -0.5 to 0.5
skp neg,DOWN	;if negative jump to DOWN
ldax db		;else high, read db
sof 1,count	;add count
wrax db,0	;write new value to db
skp zro,ENDDB	;jump to ENDDB
DOWN:
ldax db		;read db
sof 1,-count	;deduct count
wrax db,0	;write new value to db

ENDDB:

;latching switch, falling edge triggered flipflop
;Output of debounce routine of < -0.9 is low, > 0.9 is high, values in between
;are ignored and the switch does nothing, Schmitt trigger action.


ldax db			;read db
absa			;get absolute value
sof 1,-0.9		;deduct 0.9 so only values < -0.9 or > 0.9 give a positive result
skp neg,ENDSWITCH	;if negative then jump to ENDSWITCH
ldax db			;read db
sof 1,-0.9		;deduct 0.9
skp neg,LO		;if negative jump to LO, output of debounce is low 
sof 0,0.999		;else output of debounce is high
wrax mom,0		;set mom to 1 (high)
skp zro,ENDSWITCH	;jump to ENDSWITCH
LO:
ldax mom		;read mom
skp neg,ENDSWITCH	;if it's negative then debounce was already low last time so do nothing, jump to ENDSWITCH
sof 0,-0.999		;else mom was high last time so switch has only just been pressed (falling edge)
wrax mom,0		;set mom to -1 (low)
ldax latch		;read latch
sof -1,0			;invert, high becomes low, low becomes high
wrax latch,0		;write to value to latch

ENDSWITCH:

;tap tempo, uses rmp0 as a 1 Hz rising ramp, runs whilst latch is low and is sampled and held when latch is high

ldax latch		;read latch
skp neg,LOW		;if negative jump to LOW
jam rmp0		;else latch is high, jam rmp0 (reset to 0)
ldax ramp		;read ramp, will contain last value of rmp0 before latch went high	
wrax taptempo,0		;write to taptempo
skp zro,ENDTT		;jump to ENDTT
LOW:
sof 0,0.064		
wrax rmp0_rate,0		;set rmp0 rate to 1Hz
cho rdal,rmp0		;read value of rmp0
sof -2,0.999
sof 1,0.001		;level shift to 0 to 1 rising ramp
wrax ramp,1		;write to ramp
sof 1,-0.999		;deduct 0.999 from ramp		
skp neg,ENDTT		;if answer is positive then second tap hasn't happened with 0.999 ms of first			
ldax taptempo		;so keep last value of taptempo
wrax ramp,0		
sof 0,0.999		;and reset latch high
wrax latch,0
ENDTT:

;Taptempo rate indicator, creates a square wave at the tap tempo rate
sof 0,0.064		
wrax rmp1_rate,0		;set rmp1 rate to 1Hz
cho rdal,rmp1		;read value of rmp1
sof -2,0.999		;level shift to 0 - 1 rising ramp
sof 1,0.001
rdax taptempo,-0.5	;deduct half of the taptempo value
skp neg,ENDLED		;if negative skip to ENDLED
jam rmp1		;else reset ramp1
ldax led			;and invert value of led register, creates square wave at taptempo rate
sof -1,0
wrax led,0
ENDLED:

;delay

clr
rdax fback,0.95		;read feedback register, scaled to 0.95%
rdax adcl,1		;mix with input from ADCL
wra delay,0		;write to head of delay
ldax taptempo		;read taptempo register
wrax addr_ptr,0		;write to delay address pointer
rmpa 1			;read from delay address set by pointer
wrax delayout,1		;write to delayout register

mulx pot0		;mulx with pot0, feedback level control
wrax clip,-0.33333	;soft clip, using cube distortion snippet
mulx clip
mulx clip
rdax clip,1
wrax fback,0		;write to feedback register

rdax delayout,1		;read delayout register
mulx pot1		;mulx with pot1, delay level control
rdax adcl,1		;mix with input from ADCL
wrax dacl,0		;write to DACL

ldax led			;read led register
wrax dacr,0		;write to DACR, flashes LED attached to DACR
Sweetalk
Posts: 141
Joined: Thu Oct 15, 2009 5:13 am

Post by Sweetalk »

Great!! I'll try it this week and post!!
ronaldb
Posts: 19
Joined: Thu Dec 03, 2009 7:49 am

Post by ronaldb »

Hi Slacker,
THanks for this great code, seems to work oke.
But the tap tempo seems to be of tempo. Haven't checked this with a click track but when i tap in and keep counting in my head the delay does not follow.

Did you checked this?

RonaldB
slacker
Posts: 116
Joined: Tue Feb 01, 2011 1:13 pm

Post by slacker »

Did you use the latest version? The first and second versions I posted have an error in them and the delay doesn't match the tapped tempo.

In the latest one the delay time should match the tap tempo, and it seems to work for me, I haven't tested it though so there might still be some error. I'll see if I can do some tests but if you could try it with a click track and let me know what you find that would be great.
Post Reply