Any thoughts on implementing an "infinite hold" ef

Algorithm development and general DSP issues

Moderator: frank

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

Post by frank »

If you tried to reply to the email notification that will not work, you must log into the web site and reply from the message there.
Frank Thomson
Experimental Noize
livingston
Posts: 131
Joined: Sun Nov 15, 2009 3:37 pm
Location: New Orleans, LA US

Post by livingston »

Don, I had a chance to try your algo. First, your second equate statement:

;equ lpin reg1 ; filtered input

has a semicolon in front of it which needs to be deleted for things to work right.

As can be expected of this kind of thing, the tracking on a bass isn't great. Tracking a bass is notoriously difficult, so it would need some tweaking to get something useful out of it in that case. But it's definitely a start in the right direction. I'll tack a "MULX POT0" into the frequency register to see if we can get a pitch shift control, might be fun.

Thanks for the code, it'll be a great starting point for experiments with pitch tracking.

Edit:noticed another problem: you have 2 things equated to REG13. That probably explains why I was getting weird distortion - the output signal and ENVFIL were equated to the same register. I don't know why the assembler didn't catch that, seems like it shouldn't let you do that.
donstavely
Posts: 53
Joined: Thu Jan 07, 2010 2:29 pm
Location: Windsor, Colorado

Post by donstavely »

On the bass, I agree that the problem is harder. The lower frequencies are tougher in general - longer periods mean more noticeable errors and delays, since samples are sparse. I was testing only down to low E on guitar. The decay constant on the upper and lower envelope followers is the main knob you have to turn to try to scale another octave down.

The equate of the "lpin" variable was commented out because it isn't used. I tried putting a low-pass filter on the incoming signal to attenuate the 2nd harmonic to improve tracking. I didn't seem to make much difference, so I took out the code, but forget to delete the equate.

You may want to try putting the LP filter back in again for the bass. You could set the corner frequency below your lowest note and operate "on the slope". The falling amplitude at higher frequencies doesn't affect the pitch detection, as long as there is enough signal left. Then you get a 6dB reduction in 2nd harmonic across the board. Come to think of it, make it a shelving filter, so you still get some amplitude at the high end of the fretboard. The pitch detector seems to do better at the high end as it is, so it doesn't need as much "help" from the filter. I think I will try it again for guitar.

Similarly, the "envfil" variable isn't used. I was trying to filter the envelope more before applying it to the sine to clean up the sound of the demo, but I wound up tossing the code out. (It was sloppiness to leave the equate in, I was rushing a bit to add the demo oscillator code before posting.)

I think the assembler doesn't check for multiple equates because some users may want to reuse registers in their code - risky and error-prone, but maybe useful.
Don Stavely
djmalan
Posts: 36
Joined: Fri Mar 06, 2009 8:22 am

Post by djmalan »

Hi ,donstavely ,I m interested in your code .I have a question;
; Need to convert period to frequency
;
rdax avper, 1 ; get smoothed period
log -1, -0.49 ; need 1/X, the offset scales frequency
exp 1, 0 ; linear frequency
wrax freq, 0 ; save it
if I didn't miss understand this is for 1 octav down or divide by two ,isn't it?
if it's true ,how can I calculate by divide 4 and 8 or canbe said 2 and 3 octave down.
donstavely
Posts: 53
Joined: Thu Jan 07, 2010 2:29 pm
Location: Windsor, Colorado

Post by donstavely »

- "avper" is linear period in unit of time.
- take the log of this to get log period (with the FV-1 caveats about scaling)
- negate this to get log frequency (remember, we are in log space)
- add or subtract a constant from this to scale frequency (still doing log math)
- take the exp of this to get back to linear frequency (again with the caveats about scaling)

The constant I used for the demo was pretty arbitrary - just a number that causes a frequency shift. Frankly, it hurt my head to try to do the math to come up with exact numbers, but it shouldn't be impossible. If you find a constant in the log statement that matches the input pitch, then there will be a second constant that gives an octave up. Then that same interval, added or subtracted (in log space still) will give octaves up or down. (Remembering that multiplies and divides in linear space become adds and subtracts in log space.)

I don't recommend this code as a way to do pitch transposition. It is monophonic, and not perfectly reliable in tracking. It would be better to do the delay-and-ramp pitch transpose for real music signals.

People have tried to do an actual guitar synth using a pitch-to-voltage converter to drive VCO's, with a note trigger driving ADSR's, VCA's, VCF's. I am not sure how "playable" it would be.
Don Stavely
soundsubs
Posts: 24
Joined: Fri Feb 25, 2011 11:49 am

Post by soundsubs »

...any update on this?

i myself would love to see a "freeze" effect where a single cycle sample from the input is looped until a knob is twisted past a certain value or something like that.
from there we could also mess with playback speed.
donstavely
Posts: 53
Joined: Thu Jan 07, 2010 2:29 pm
Location: Windsor, Colorado

Post by donstavely »

I have not worked on this in a while. First, I switched to a delay-based approach so avoid tracking issues and the single-note limitation. Seancostello's suggestion earlier in this thread is the most promising approach. Second, Electro-Harmonix came out with the Freeze. This is exactly the effect that I was going for.

Sorry,
Don
Don Stavely
soundsubs
Posts: 24
Joined: Fri Feb 25, 2011 11:49 am

Post by soundsubs »

getting close to nailing this, but its constantly outputting the same delay time, about 1 second, even though i defined mem at 65. ive tried all sorts of values, nothing changes the time.


; this is borrowed heavily from the delay/play alg posted by frank
; meant to be a "freeze" effect where it samples input and
; outputs a shortened loop of the input audio, near a single cycle.
; If POT0 is 0 to 0.5 we are in record mode
; If 0.5 to 0.999 we are in playback mode
;
delL mem 65 ; Define delay as really really small
ldax pot0 ; Read in POT0
sof 1.0,-0.5 ; Add -0.5 to the POT0 value so it ranges -0.5 to +0.5
skp gez,play ; If ACC is >=0 we are in playback mode so jump to play
ldax adcl ; Read in ADC left
wra delL,1.0 ; Write to left delay line
wrax dacl,0 ; and to DAC left
skp zro, end ; Jump to end
;
; Play back mode
play:
clr ; Clear ACC
rda delL#,1.0 ; Read tail of left delay
wrax dacl,0 ; Write it to DAC left
;
end:
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

(Not in office so haven't tried the changes so may be a typo in it but the logic is correct...)

You are working with a single circular buffer in memory in this case so you want to clear the end of the buffer each cycle so you could try:

delL mem 65 ; Define delay as really really small
ldax pot0 ; Read in POT0
sof 1.0,-0.5 ; Add -0.5 to the POT0 value so it ranges -0.5 to +0.5
skp gez,play ; If ACC is >=0 we are in playback mode so jump to play
ldax adcl ; Read in ADC left
wra delL,1.0 ; Write to left delay line
wrax dacl,0 ; and to DAC left
wra delL#,0 ; write 0 at end of delay line to limit record length
skp zro, end ; Jump to end
;
; Play back mode
play:
clr ; Clear ACC
rda delL#,1.0 ; Read tail of left delay
wrax dacl,0 ; Write it to DAC left
;
end:


But there is a problem with this because it still plays through the entire memory so we hear a short period of sound so we need to also limit the playback to only the buffer area:

delL mem 65 ; Define delay as really really small
ldax pot0 ; Read in POT0
sof 1.0,-0.5 ; Add -0.5 to the POT0 value so it ranges -0.5 to +0.5
skp gez,play ; If ACC is >=0 we are in playback mode so jump to play
ldax adcl ; Read in ADC left
wra delL,1.0 ; Write to left delay line
wrax dacl,0 ; and to DAC left
wra delL#,0 ; write 0 at end of delay line to limit record length
skp zro, end ; Jump to end
;
; Play back mode
play:
clr ; Clear ACC
rda delL#,1.0 ; Read tail of left delay
wra delL,1.0 ; Write to head of delay
wrax dacl,0 ; Write it to DAC left
wra delL#,0 ; write a 0 to end of delay, not really needed but will make sure rest of memory is 0 if you add other functions
;
end:
Frank Thomson
Experimental Noize
soundsubs
Posts: 24
Joined: Fri Feb 25, 2011 11:49 am

Post by soundsubs »

thanks for the reply, frank.

this doesnt really work. this loops a pitched waveform that sounds a lot like a sine wave and which is usually the same thing despite various input sounds.
ive played with it for 15 minutes or so and i would say 80% of the time it puts out the same sine wave.

i toggle between PLAY and PASSTHRU
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

I'll try to code in the office tomorrow but try making the delL length much longer (i.e. 8192), 65 is really short
Frank Thomson
Experimental Noize
soundsubs
Posts: 24
Joined: Fri Feb 25, 2011 11:49 am

Post by soundsubs »

doh you did it!
a length of 2048 or 1024 is great for this!

now to make pot1 determine length of buffer and i'll post it back.
soundsubs
Posts: 24
Joined: Fri Feb 25, 2011 11:49 am

Post by soundsubs »

ok i did something wrong.
now sound cuts off after pot0 goes above 0

Code: Select all

; this is borrowed heavily from the delay/play alg posted by frank
; meant to be a "freeze" effect where it samples input and 
; outputs a shortened loop of the input audio, near a single cycle. 
; If POT0 is 0 to 0.5 we are in record mode 
; If 0.5 to 0.999 we are in playback mode 
; 
delL mem 2048 	; Define delay as kinda small 
ldax pot0 	; Read in POT0 
sof 1.0,-0.5 	; Add -0.5 to the POT0 value so it ranges -0.5 to +0.5 
skp gez,play 	; If ACC is >=0 we are in playback mode so jump to play 
ldax adcl 	; Read in ADC left 
wra delL,1.0 	; Write to left delay line 
wrax dacl,0 	; and to DAC left 
wra delL#,0 	; write 0 at end of delay line to limit record length 
skp zro, end 	; Jump to end 
; 
; Play back mode 
play: 
clr 		; Clear ACC 
ldax pot1	; read POT1 value
wrax reg0,1	; write the value to reg0, save it for later
rda delL#,1 	; Read tail of left delay 
mulx reg0	; multiply this * reg0 which is pot1
wra delL,1 	; Write to head of delay
wrax dacl,0 	; Write it to DAC left 
wra delL#,0 	; write a 0 to end of delay, not really needed but will make sure rest of memory is 0 if you add other functions 
; 
end:
frank
Posts: 1244
Joined: Wed Oct 19, 2005 12:26 pm
Contact:

Post by frank »

Now, I'm going to be a bit of a pain here :wink: and force you to do two thing:

First, you don't say what you are trying to do with pot 1. I can tell from the code but making you get in the habit of explicitly stating the intent of any input or controller, helps a lot for others to help debug.

Second, the problem is here:
wrax reg0,1 ; write the value to reg0, save it for later

Consider what is going into the accumulator, what you are doing in the next instruction and what you need to change in this 'wrax' line.
Frank Thomson
Experimental Noize
soundsubs
Posts: 24
Joined: Fri Feb 25, 2011 11:49 am

Post by soundsubs »

well i was able to comply with the first one easy enough.
i really dont know what to do, other than change the line ahead of it, but thats not what i want either.

Code: Select all

; this is borrowed heavily from the delay/play alg posted by frank 
; meant to be a "freeze" effect where it samples input and 
; outputs a shortened loop of the input audio, near a single cycle. 
; If POT0 is 0 to 0.5 we are in record mode 
; If 0.5 to 0.999 we are in playback mode 
; POT1 will be used to determine length of delay buffer that is read. 
;
delL mem 2048   ; Define delay as kinda small 
ldax pot0    	; Read in POT0 
sof 1.0,-0.5    ; Add -0.5 to the POT0 value so it ranges -0.5 to +0.5 
skp gez,play    ; If ACC is >=0 we are in playback mode so jump to play 
ldax adcl    	; Read in ADC left 
wra delL,1.0    ; Write to left delay line 
wrax dacl,0     ; and to DAC left 
wra delL#,0     ; write 0 at end of delay line to limit record length 
skp zro, end    ; Jump to end 
; 
; Play back mode 
play: 
clr       	; Clear ACC 
rdax pot1,1  	; read POT1 value 
wrax reg0,1   	; write the value to reg0, save it for later
rda delL#,1    	; Read tail of left delay 
mulx reg0   	; multiply this * reg0 which is pot1 
wra delL,1    	; Write to head of delay 
wrax dacl,0    	; Write it to DAC left 
wra delL#,0    	; write a 0 to end of delay, not really needed but will make sure rest of memory is 0 if you add other functions 
; 
end: 
Post Reply