Microphone to Headphone Demo with 140uSec delay
stephenwagner
Posts: 147
I have been trying to build a simple 140 uSec audio delay for some time.
I understand the microphone to headphone sigma delta ADC and DAC demo and counters.
I need help with reading data from cog RAM and writing data to cog RAM.
Set up an array of 11 locations. At 10 bits and a sample rate of 78 Khz or 12.82 uSec sample rate * 11 I should be able to achieve a 141 uSec delay.
Read data from location 0, write data to DAC, read data from ADC, write data to location 0. waitcnt·· asm_cnt,asm_cycles
Read data from location 1, write data to DAC, read data from ADC, write data to location 1. waitcnt·· asm_cnt,asm_cycles
.
.
.
Read data from location 10, write data to DAC, read data from ADC, write data to location 10. waitcnt·· asm_cnt,asm_cycles
Read data from location 0, write data to DAC, read data from ADC, write data to location 0. waitcnt·· asm_cnt,asm_cycles
Repeating in an endless loop.
I have been reading the PE kit examples and the Stereo objects. This stuff is new to me.
I have the ADC working on a bread board and made the necessary changes to the DAC lowpass filter for my application.
Any help would be appreciated.
Stephen
I understand the microphone to headphone sigma delta ADC and DAC demo and counters.
I need help with reading data from cog RAM and writing data to cog RAM.
Set up an array of 11 locations. At 10 bits and a sample rate of 78 Khz or 12.82 uSec sample rate * 11 I should be able to achieve a 141 uSec delay.
Read data from location 0, write data to DAC, read data from ADC, write data to location 0. waitcnt·· asm_cnt,asm_cycles
Read data from location 1, write data to DAC, read data from ADC, write data to location 1. waitcnt·· asm_cnt,asm_cycles
.
.
.
Read data from location 10, write data to DAC, read data from ADC, write data to location 10. waitcnt·· asm_cnt,asm_cycles
Read data from location 0, write data to DAC, read data from ADC, write data to location 0. waitcnt·· asm_cnt,asm_cycles
Repeating in an endless loop.
I have been reading the PE kit examples and the Stereo objects. This stuff is new to me.
I have the ADC working on a bread board and made the necessary changes to the DAC lowpass filter for my application.
Any help would be appreciated.
Stephen
Comments
Otherwise you'll need to do self modifying code to have arrays.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Nyamekye,
I am new to this thing called a propeller. It is the syntax I can never get straight.
A simple code snipit would realy help. Then I can build from there.
I have done this with the stamp II, but never with the propeller.
Stephen
As usual, my internet terminal is not my Propeller programmer so I just
made up code similar to how I think you are doing it. Untested and likely to have typos.
Don't change the readout-samplein code you already wrote, just add the delay instructions.
I can not rewrite it as a short loop at this moment, which would make it 12x shorter.
Basically it would use another long as a pointer from sample 0 to sample 11, add #1 to it,
and use self-modifying code against the mov (or movd or movs) instructions,
and after sample11 it would be changed back to sample00.
Others would probably use WAITCNT instead of the DJNZ for a delay although it
might need more than 2 instructions, I'm not sure. WAITCNT is more predictable for
exact timing than DJNZ loops. I apologize about not finishing the delay effect I was
working on last time I commented on your project.
Again if you have 2 inputs on your ADC, passthrough the undelayed channel so the
quality of both channels is the same. I can't write the code now that basically moves
data directly from your ADC to DAC before each delay step.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
VIRAND, If you spent as much time SPINNING as you do Trolling the Forums,
you'd have tons of awesome code to post! (Note to self)
I will study your code and make the necessary changes and mods.
Do I need to declair "sample00 ... sample10" as an array?
What is "fit 406"??
SJW
uses too many longs to fit in a cog, which is more than 496.
You don't need an array, or you do, but it just exists without being declared except by making the longs for it.
If you figure out how to shrink the program into a loop (which I think requires self modifying code and/or a long
reserved as an index pointer) then you probably just count the pointer from #sample00 to #sample 00+11
and don't need to label all those longs. The loop could also be done using djnz on a register (a labeled long)
after setting it to 12 if you add or subtract 1 from the pointer while working on the samples.
Sorry again my browser is not near my propeller. It is easier for me to write code if I can see if it works or not.
I am too disorganized to be as helpful as possible now. (I'm not employed by Parallax, but I do try to help.)
·
I have my 10 bit audio delay working. 78 KHz sample rate or about 3.9 samples at 20 KHz. The ADC and DAC are built on the Prop. USB board. Most of the Left and Right audio processing is done in the analog world. For my application and listing environment less then 140uSec of delay was required. I also took advantage of reducing the output amplitude by -6dB power, -3dB voltage by not quite shifting all the bits to the·left in the DAC counter. All the delay takes place in COG memory. Its messy. I need to work the issues with HUB memory and reducing the code to a loop. It looks like there is plenty of clock overhead. Once I get the HUB memory issues worked out, I plan to build this thing by replacing the analog processing with software and another GOG or two.
·
In the analog world both the left and right are buffered in and then out. The left and right are also subtracted using an op-amp to create a L-R signal. The L-R signal is then delayed by the prop. USB ADC-DAC. The delayed L-R is then split with an op-amp to create a delayed L-R and a -L+R signal. The L-R signal is recombined with the un-delayed L audio signal and the -L+R is recombined with the un-delayed R audio channel to creat the effect I have been looking for.
·
The best way to describe the effect is to place your speakers 4 to 6 feet apart, stand 6 to 8 feet away from them, and listen to Pink Floyd Time. Close your eyes. The speakers disappear and the clocks are presented as if they are 6 to 8 feet away and on a board 6 to 8 feet wide. Each clock is spatially presented where it belongs. It is a neat effect.
·
{{
CON
································· 3.3V
·································
································· │
································ 1nF
··························· 100KΩ │
······· ··············6··· ────┫ 100KΩ 0.1µF
································· ┣──────── In
····················· 11·· ──────┫
································· │
································ 1nF·········
································· │
······ ···························
·
································· 3.3V
·································
································· │
································ 1nF
································· │
································· │·· 0.1µF
··························· 4.7KΩ ┣───── Out·
······················· 3·· ───┫
································· │
································ 1nF
································· │
·································
}}
CON
· _clkmode = xtal1 + pll16x
· _xinfreq = 5_000_000
· bits = 10·············· 'try different values from table here
·
PUB go
· cognew(@asm_entry, 0)·· 'launch assembly program into a COG
DAT
'
'
' Assembly program
'
············· org
asm_entry···· mov···· ··dira,asm_dira·················· 'make pins 8 (ADC) and 0 (DAC) outputs
············· movs····· ctra,#6························ 'POS W/FEEDBACK mode for CTRA
············· movd····· ctra,#11
············· movi····· ctra,#%01001_000
············· mov····· ·frqa,#1
············· movs····· ctrb,#3······················· 'DUTY DIFFERENTIAL mode for CTRB
············· 'movd····· ctrb,#11
············· movi····· ctrb,#%00110_000
·············
············· mov······ asm_cnt,cnt···················· 'prepare for WAITCNT loop
············· add······ asm_cnt,asm_cycles
·············
:loop········ waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay1,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay1
············· add······ asm_old,asm_sample
············· mov······ delay1, asm_sample
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa········· ·······'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay2,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay1
············· add······ asm_old,asm_sample
······ ·······mov······ delay2, asm_sample
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
··········· ··sub······ asm_sample,asm_old
············· shl······ delay3,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay3
············· add······ asm_old,asm_sample
············· mov······ delay3, asm_sample
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl··· ···delay4,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay4
············· add······ asm_old,asm_sample
············· mov······ delay4, asm_sample
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
···· ·········shl······ delay5,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay5
············· add······ asm_old,asm_sample
············· mov······ delay5, asm_sample
············· waitcnt·· asm_cnt,asm_cycles········· ····'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay6,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay6
············· add······ asm_old,asm_sample
············· mov······ delay6, asm_sample
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay7,#31-bits············ 'justify sample and output to FRQB
············· mov···· ··frqb,delay7
············· add······ asm_old,asm_sample
············· mov······ delay7, asm_sample
············· jmp······ #:loop
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
····· ········mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay8,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay8
············· add······ asm_old,asm_sample
············· mov······ delay8, asm_sample
············· 'jmp······ #:loop
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov· ·····asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay9,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay9
············· add·· ····asm_old,asm_sample
············· mov······ delay9, asm_sample
············· 'jmp······ #:loop
············· waitcnt·· asm_cnt,asm_cycles············· 'wait for next CNT value (timing is determinant after WAITCNT)
············· mov······ asm_sample,phsa················ 'capture PHSA and get difference
············· sub······ asm_sample,asm_old
············· shl······ delay10,#31-bits············ 'justify sample and output to FRQB
············· mov······ frqb,delay10
············· add······ asm_old,asm_sample
············· mov······ delay10, asm_sample
·
············· 'jmp······ #:loop························· 'wait for next sample period
'31 30 29 28_27 26 25 24_23 22 21 20_19 18 17 16_15 14 13 12_11 10 9 8_7 6 5 4_3 2 1 0
' 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0· 0__1· 0 0 0_0 0 0 0_1 0 0 0
'
' Data
'
asm_cycles··· long····· |< bits - 1···················· 'sample time
asm_dira····· long····· $00000808······················ 'output mask
asm_cnt······ res······ 1
asm_old······ res······ 1
asm_sample··· res······ 1
delay1······· res······ 1
delay2······· res······ 1
delay3······· res······ 1
delay4······· res······ 1
delay5······· res······ 1
delay6······· res······ 1
delay7······· res······ 1
delay8······· res······ 1
delay9······· res···· ··1
delay10······ res······ 1
·
Well my code did not post well. I need to work this issue also.
SJW
which is also in the vocal tract singing objects, monks and seven.
I think that the object might do what you want.
I seem to remember that it implements 2 delays in longs which are tapped by putting 1 bits in them.
You might only have to put a 1 bit at the beginning of a long,
and another 1 bit somewhere in the middle of another long.
Maybe this is too late since you probably soldered up all the analog stuff already,
and I don't remember enough about the object to know if it can be set to 120uS.
There were a lot of cool analog hack effects in the 1900s.
Floyd's Clocks ... Was that designed for this effect, or just coincidentally great with it?
One of my friends who is more audio-equipped than myself sometimes argues about
the complexity of effects, in terms of how he would do them on his DAW, and while
discussing an idea he thought was absolutely impossible, it suddenly became apparent
that it was hard for him to synth a stereo track in real time, when I mentioned the
head-orbiting-squeak effect in Moody Blues - Search for the lost chord - The Best Way to Travel.
I imagine that it must have been made with tiny mics inside of ears, because the stereo
earlobe reflections for front and back sense is apparently very hard to compute,
given a sound and relative position coordinates. It would seem obvious in quadraphonic,
with a dual-pot joystick.
SJW
Here are 2 code snippets which shows you how to do it with an array in cog ram or in hub ram:
they are just written down and not tested!
Andy
Post Edited (Ariba) : 2/20/2010 10:19:12 AM GMT
I have been working the PE Examples. It is difficult to understand how the parameters and results are passed around.
I will study your provided code and work it into my next project. Stereo in with two knobs. Amplitude and delay.
I understand the spatializer only samples at about 20KHz and or 2 samples at 10KHz.
Someday I will figure that thing out.
SJW
I am curious about the need for 140 usecs of delay.· At the speed of sound, this is less than 2 inches of travel time.· It seems like 140 usecs wouldn't produce a dramatic spatial effect.· However, subtracting attenuated values of the left and right signals from the opposite channels would have a big effect.· It seems like that is the major component to the spatial effect, and not the delay.
I noticed your code allows for other delays.· Have you tried the other delays, and do you notice a difference in the spatial effect using different delays?
Dave
I found the idea in a Radio Electronics article back·in 1985?. It was done with BBD devices and op-amps. The work was done by Cohen.
I built the unit and it actually worked. If you listen to music that was recorded properly, such as jazz and or classical, all the instruments are place correctly in the sound field. I listen to Reggie, go figure. ·What I learned is frequency response is only a small part of the listening enjoyment. I have a copy of the article.
Here is the link to the US Patent.
http://www.freepatentsonline.com/4308423.pdf
http://www.freepatentsonline.com/pdf_collections_server1/uspt/patent_pdf/4308/US4308423/pdf/US4308423.pdf?k=c4a32c374ed9c2dff4f592b454c50a78
Here is a collection of patents. http://www.freepatentsonline.com/4748669.html
As I understand it: If a sound is reproduced in the left speaker it arrives in the left ear first and then the right ear some time later and at a reduced amplitude.·If the same left sound is reproduced in the right speaker with the following characteristics: 1. Left sound -6dB, -180deg phase. 2. Left sound with slightly more delay then would naturally occur from the left speaker. The sound from the left speaker would appear to be further to the left then the actual speaker location. If you sum all the signals to the right speaker, it works out to approximately 140uSec. With this method, only 140uSec of delay is necessary to create the effect.
·
I have adjusted my delay to my environment. Disconnect one input, but not the output,·and adjust the delay and amplitude until the sound is placed at a comfortable position. Walls, ceilings all have an effect. My listening environment required less the 140uSec and about -8dB of delay signal.
·
Here is another interesting patent without the delay, but two additional speakers placed further appart to creat the delay.
http://www.freepatentsonline.com/4489432.pdf
·
I hope this helps.
SJW
So in essence, you are trying to cancel the sound from the other speaker at each ear by subtracting a delayed and antennuated version of the sound from the opposite speaker.· I'm don't understand the purpose of also adding the delayed and antennuated left and right signals to each other, except that you only have to delay a single difference signal instead of delaying two independent signals.
Edit: I just noticed that you are referring to headphones in the subject line.· Are you using headphones or speakers?· The one patent I looked at was referring to speakers.
Post Edited (Dave Hein) : 2/22/2010 4:36:55 PM GMT
Dave,
The post·should have started as just a delay. The headphone example with the ADC to DAC was a convenient starting point.
·
The delay is necessary. If you physically move the speaker further to the left, the sound will reach the right ear a little later on the order of uSec. The circuit artificially creates the same effect without moving the speaker. I have been doing this with speakers. I have not tried headphones. The idea works. With the circuit installed between pre-amp out and main amp in, while listing to Pink Floyed Time, Close your eyes, you can point your finger at each clock. One clock is to the left of the left speaker. One clock is to the right of the right speaker. Another clock is at the left speaker, another at the right speaker. Others have filled in the rest of the sound field between the speakers. You need to hear it.
SJW
We are in agreement about the delay, I just wasn't sure it's necessary to add the delayed/attenuated signal to it's own signal while also subtracting the delayed/attenuated opposite signal.· With a pair of headphones the left ear will hear the left signal, and the right ear will hear the right signal.· With speakers, the listener will hear the following:
Le(t) = Ls(t-T1) + a*Rs(t-T2)
Re(t) = Rs(t-T1) + a*Ls(t-T2)
Where Le(t) and Re(t) represent the audio heard at the left and right ears, and Ls(t) and Rs(t) is the audio from the left and right speakers.· The value "a" represents an attenuation factor applied to the opposite speaker because the ear picks up more volume from the speaker on the same side.· The times T1 and T2 represent the time it takes for the sound to travel from the speakers to the ears.· T2 is greater than T1 because the opposite speaker is slightly further away than the speaker on the same side as the ear.· In your configuration, the difference between T2 and T1 is 140 usecs.
To me, it seems like the circuit should attenuate and delay each channel independently instead of the difference.· If we do each channel independently then the speakers will emit the following signals
Ls(t) = L(t) - a*R(t-DT)
Rs(t) = R(t) - a*L(t-DT)
and the ears will hear the following signals
Le(t) = L(t-T1) - a*R(t-T2) + a*R(t-T2) - a*a*L(t-T1-2*DT)
Le(t) = L(t-T1) - a*a*L(t-T1-2*DT)
Re(t) = R(t-T1) - a*L(t-T2) + a*L(t-T2) - a*a*R(t-T1-2*DT)
Re(t) = R(t-T1) - a*a*R(t-T1-2*DT)
This produces the original left and right signals with quieter echoes that are delayed by 2*DT.· If we were to add the delay and attenuated difference signal, then the speakers would produce
Ls(t) = L(t) - a*(R(t-DT) - L(t-DT))
Rs(t) = R(t) - a*(L(t-DT) - R(t-DT))
and we would hear
Le(t) = L(t-T1) - a*R(t-T2) + a*L(t-T2) + a*R(t-T2) - a*a*L(t-T1-2*DT) + a*a*R(t-T1-2*DT)
Le(t) = L(t-T1) + a*L(t-T2) - a*a*L(t-T1-2*DT) + a*a*R(t-T1-2*DT)
Re(t) = R(t-T1) - a*L(t-T2) + a*R(t-T2) + a*L(t-T2) - a*a*R(t-T1-2*DT) + a*a*L(t-T1-2*DT)
Re(t) = R(t-T1) + a*R(t-T2) - a*a*R(t-T1-2*DT) + a*a*L(t-T1-2*DT)
These results seem to be a less accurate approximation to the original left and right signals.· However, maybe the extra components produce a more lively and pleasing result.
Dave
·
In the 1960s and 1970s there was a whole different creativity with tinkering with analog recording than what
is common with digital. The two inches delay is very significant with the earlobes, although the patent may
have a different explanation. The earlobes are significant because underwater you lose the stereophonic sense
unless you wear 5 times bigger artificial earlobes because sound goes 5 times faster.
It seems obvious to me that delay is more significant than loudness for stereolocation sense, but less obvious
that a two inch delay would be noticeable, except for the earlobe involvement in that sense.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
I should be typing in Spin now.
Coming soon. My open Propeller Project Pages and favorite links index.
·
Are you an acoustic engineer??? Your explanation is slightly over my head. And that is ok.
·
My grandfather had done some work for Radio City Music Hall while working for Western Electric.
·
I have an old Western Electric 8-Ball 630A microphone he gave me when I was a child. It is in working order and in mint condition. Don't know why he gave it to me, I am kicking myself in the [url=mailto:!@#$%^&*]!@#$%^&*[/url] for pushing holes in his Western Electric woofers.
·
Anyway, thanks for everyone’s inputs comments with my project. I don't like using code that I do not understand. I am still working/studying the code that was posted by Ariba. I have also been studying the code in the PE examples and the MIC to VGA example.
·
My goal is to pass the L & R through the prop, create the L-R delay, sum it all back up and add some knobs/buttons/switches and a display for the delay time and amplitude. I also want to keep it at 10 bits and 78KHz sample rate. The hole project in software.
SJW
I did listen to this thing with headphones for the first time last night. Interesting. The sound field·was placed directly between the ears. Jazz. Nothing seemed to be anoing and or overcompensated. I did discover that I may actually be overdriving the ADC while listing with the headphones. That I can fix with a resister sustitution (-3dB voltage, -6dB power) and shift to the left one bit in the DAC software (+3dB voltage, +6dB power).
SJW
Keep us updated on your project.· It looks very interesting.
Dave