Rough 'n Ready Audio Frequency extraction with a PIC


Closed Thread
Results 1 to 34 of 34

Hybrid View

  1. #1
    Join Date
    Mar 2009
    Posts
    653

    Default Rough 'n Ready Audio Frequency extraction with a PIC

    I've read a fair bit about this, but I'd like a bit of guidance & a few concepts confirmed or corrected!

    I'd like to be able to establish which note is being played on a guitar. I'm only interested from the 'D' string upwards.

    The D string frequency is 146.8Hz - which means that one cycle takes 6.81ms. Ok, that's the bit I'm sure about - the rests is fuzzy !

    I don't want a lot of complication in the supporting electronics -so ideally I'd like to use the internal oscillator (yes, I know it's rough & ready & not particularly accurate, but I'm not going to be using this to tune the guitar).

    I'd thought about using the comparator & basically counting how many cycles have been received in a given time frame - I'd like the time frame to be as low as possible.

    Re PICs & floating point - I believe PICs don't do FP - but I'm not sure of the impact to my situation....I'm figuring that the PIC dispenses with the decimal places & just give the integer? (does it round up or just ignore them all together?)

    I've created a table to show the lowest possible sampling time frame to be able to get a unique integer for each note, which for this situation works out at about 100ms...

    (btw: I didn't know how to format the Excel cells to look ok on here, so I used the code tags - which seems to work ok!)

    Code:
    Note	Freq		Cycles in 100mS
    D	146.83		14.683
    D#	155.56		15.556
    E	164.81		16.481
    F	174.61		17.461
    F#	185		18.5
    G	196		19.6
    G#	207.65		20.765
    A	220		22
    A#	233.08		23.308
    B	246.94		24.694
    C	261.63		26.163
    C#	277.18		27.718
    D	293.67		29.367
    D#	311.13		31.113
    E	329.63		32.963
    F	349.23		34.923
    F#	369.99		36.999
    G	392		39.2
    G#	415.3		41.53
    A	440		44
    A#	466.16		46.616
    B	493.88		49.388
    C5	523.25		52.325
    C#	554.37		55.437
    D	587.33		58.733
    D#	622.25		62.225
    E	659.26		65.926
    F	698.46		69.846
    F#	739.99		73.999
    G	783.99		78.399
    E	329.63		32.963
    F	349.23		34.923
    F#	369.99		36.999
    G	392		39.2
    G#	415.3		41.53
    A	440		44
    A#	466.16		46.616
    B	493.88		49.388
    C5	523.25		52.325
    C#	554.37		55.437
    D	587.33		58.733
    D#	622.25		62.225
    E	659.26		65.926
    F	698.46		69.846
    F#	739.99		73.999
    G	783.99		78.399
    G#	830.61		83.061
    A	880		88
    A#	932.33		93.233
    B	987.77		98.777
    C	1046.5		104.65
    C#	1108.74		110.874
    D	1174.66		117.466
    D#	1244.5		124.45
    E	1318.52		131.852
    As you can see, by using 100mS, the sample period 'integer' is unique (ie for the number of complete cycles completed).

    It should then be a case of a simple Look up Table to work out which note is being played, eg...

    If Sample count = 14 then D note is being played.
    if Sample count = 22 the 'A' note is being played.

    And so on.

    So at this embryonic stage...

    Is my line of thought correct?

    Is there any clever workaround available meaning I could bring that 100mS sampling period down?
    Last edited by HankMcSpank; - 7th March 2010 at 11:15.

  2. #2
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,653


    Did you find this post helpful? Yes | No

    Wink Make it simple ???

    Hi, Hank

    Why not use a simple NE 567, which will do all the job ???

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  3. #3
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Acetronics View Post
    Hi, Hank

    Why not use a simple NE 567, which will do all the job ???

    Alain
    Were the fun in that?!

    Seriously, I've already got a PIC in my circuit with a fair amount of unused pins...I could quite readily utilize one of those pins to give me the note info - I don't need much in the way of granularity - just frequency resolution down to rough semitones, which I reckon the PIC I'm using ought to be able to establish reasonably easily. I also don't want to add in more components if possible (hence using the PIC since it's already there!)

  4. #4
    Join Date
    Jan 2009
    Location
    Miami, Florida USA
    Posts
    699


    Did you find this post helpful? Yes | No

    Default

    originally posted by hankmcspank

    Were the fun in that?!
    ???????????????

    Robert

  5. #5
    Join Date
    May 2004
    Location
    NW France
    Posts
    3,653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by rsocor01 View Post
    ???????????????

    Robert

    just wait and see ...

    and finally laugh.

    Alain
    ************************************************** ***********************
    Why insist on using 32 Bits when you're not even able to deal with the first 8 ones ??? ehhhhhh ...
    ************************************************** ***********************
    IF there is the word "Problem" in your question ...
    certainly the answer is " RTFM " or " RTFDataSheet " !!!
    *****************************************

  6. #6
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by rsocor01 View Post
    ???????????????

    Robert
    I meant if simply used dedicated ICs to solve each and every one of my needs - I wouldn't learn anything about PIC programming (the fun bit)

    Quote Originally Posted by Acetronics View Post
    just wait and see ...

    and finally laugh.

    Alain
    it was a meant to be a quip.

    If I'm to grasp PIC programming, then I need a small bite sized goal/challenges (& grasp some concepts)...sure there's normally dedicated h/w for just about everything nowadays, but I won't learn much if I simply throw in a dedicated IC for each & every requirement....hence my line of questiong & reluctance to add in extra h/w,
    Last edited by HankMcSpank; - 7th March 2010 at 13:45.

  7. #7
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530


    Did you find this post helpful? Yes | No

    Default

    Hank, if you have a garbage can handy, bring it next to your computer before you read on........

    Name:  404.jpg
Views: 1481
Size:  44.1 KB

    It looks like you are on the right track to me. If you like your decimals, use the 50 million instead of the 500,000. Then, when you go to print out your number, you can add your decimal back in like this.

    Code:
    LCDOut dec frequency/100,".",dec frequency//100," Hertz"
    


    For more nausea, check out http://members.cox.net/mathmistakes/music.htm

    Not sure if this could help you in any way. A lookup is probably the way to go, but I think this is interesting about the mathematical relationship between the notes.

    There are some
    important mathematical relationships between the notes played in music and the frequency of those notes.
    There are two constant values in music. The first is that the A note that is 9 white keys below middle C has
    a frequency of 440 hz. The second constant value in music is the 12th root of 2 (1.0594630943593...) which is the ratio of the frequencies between half tones. So, the frequency of A# is 440 × 1.059... = 466.16376... The
    frequency of B is 466.1637 × 1.0594 = 493.8833. After you do this 12 times you end up with A an octave higher
    which equals 880hz. Doubling the frequency creates a note an octave higher. Reversely, dividing the frequency in
    half creates a note an octave lower.
    I think you might be able to do some interesting things with the constant. The tool to convert things like that to fractions a PIC can use is here: http://www.miscel.dk/MiscEl/miscel.html

    Name:  miscel.PNG
Views: 1887
Size:  29.4 KB

    Then you could possibly use 3118/2943 or 196/185 get to that constant. Again, not sure that would be useful for a tuner. Maybe more useful for tone generation.

    Walter
    http://www.scalerobotics.com

  8. #8
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by scalerobotics View Post

    It looks like you are on the right track to me. If you like your decimals, use the 50 million instead of the 500,000. Then, when you go to print out your number, you can add your decimal back in like this.

    [code]
    LCDOut dec frequency/100,".",dec frequency//100," Hertz"[FONT=Verdana]
    Nice one & that suggestion of yours puts the decimal point in nicely...
    Code:
    Count    Converted to frequency...
    1935	258.39 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    1937	258.13 Hertz
    1936	258.26 Hertz
    1936	258.26 Hertz
    1935	258.39 Hertz
    1935	258.39 Hertz
    (My sig gen actually shows 258.4 on its LED readout when the above was seen)

    The closest musical note to 258.3(ish)Hz is C (261.63Hz) ....the semi-tones either side of C, are B (246.94Hz) & C# (277.18Hz) ....so the error of ± 0.2Hz I'm seeing above isn't likely to be that much of a problem in the great scheme of things.

    Many thanks for all your input - I might now just go off on a tangent & have a dabble with guitar to midi!
    Last edited by HankMcSpank; - 28th August 2010 at 16:01.

  9. #9
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530


    Did you find this post helpful? Yes | No

    Default

    I just noticed that my answer will overflow when at the higher frequencies. So for a single decimal point it should stay within limits throughout the range you gave in post 1.

    Code:
    b=5000
    c=1000
    dummy = b * c       '5,000,000 
     frequency = div32 mytime
    LCDOut dec frequency/10,".",dec frequency//10," Hertz"
    Last edited by ScaleRobotics; - 28th August 2010 at 16:40.
    http://www.scalerobotics.com

  10. #10
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    Cool Walter.

    All is well (± 0.1Hz)...


    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.2Hz
    154.2Hz
    154.3Hz
    154.3Hz
    154.3Hz
    154.3Hz


    So, for anyone who may find this thread via a search, to summarize here's the code to frequency detect audio signals fed into pin 7 of a 16f690 (using the PIC's internal comparator to generate interrupts, which are in turn 'timed' successively)....

    Code:
    @ __CONFIG _FCMEN_OFF & _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _BOR_OFF & _PWRTE_OFF
    '***********************************************************************************************
    
    '16F690 Pin Connections....
    ' PIN# NAME     USE & CONNECTION
    ' 1   Vdd      +5VDC power supply   
    ' 7   RC3      C12IN3- (external comparator input)
    '10   RB7     HSEROUT Pin (fed into Pickit2's pin 4 to display data onsreen via the Pickit2's UART tool)
    '19   RA0      Ext VREF for Comparator1   
    ' 20  Vss      Ground
    '*************************************************************************************
    DEFINE  OSC 4          ' set Oscillator at 4Mhz.
    DEFINE  NO_CLRWDT 1   ' PBP doesn't clear WDT automatically
    DEFINE HSER_SPBRG 25   'HSEROUT Stuff.
    DEFINE HSER_TXSTA 24h  'HSEROUT Stuff.
    DEFINE HSER_CLROERR 1  'HSEROUT Stuff.
    txsta = %10100100       'setup the tx register
    RCSTA.7 = 1             ' Enable RB7 for TX USART
    
    INTCON.0 = 0            ' clears the RABIF Flag (to 0), COULD be 1 on reset (unique to F690)
    ANSEL      = 0    'disable AtoD.
    ANSELH     = 0    'disable AtoD.
    CM2CON0 = 0   'turn off comparator 2.
    
    'Turn on & Set up Comparator 1
    CM1CON0 = %11100011    'Comparator ext op pin disabled (op of comparator avaible internally only), compare against external VREF
    
    MyTime       var  word    ' used to amalgamate TMR1 High & Low Bytes.
    Frequency    var  word    'used to convert the 'count' to frequency.
    
    ' the following is pretty much a straight lift from the compiler manual (DIV32 section)
    a Var Word
    b Var Word
    c Var Word
    dummy Var Word
    
    'these two below will later set the dummy variable to total  5,000,000 in the interrupt handler for DIV32 to use
    b = 5000   
    c = 1000
    
    MyTime = 0 'clear down Mytime, prior to starting.
              
    INCLUDE "DT_INTS-14.bas"     ' Base Interrupt System  PO90OOO9
    INCLUDE "ReEnterPBP.bas"     ' Include if using PBP interrupts
    
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   CMP1_INT,  _Comp1_Int,   PBP,  yes
        endm
        INT_CREATE               ; Creates the interrupt processor
    ENDASM
    T1CON = $11               ; Prescaler setting - this one means 2us between successive clocks
    
    @ INT_ENABLE  CMP1_INT     ; enable Comparator 1 interrupts
    
    T1CON.0=0 'stop the timer
    TMR1H = 0 'Set the high part of the timer value to 0
    TMR1L = 0 'Set the low part of the timer value to 0
    
    
    
    'Main body of (dummy) Code*********************************************************************************************
    Main:
            pause 10
            goto Main
            end
       
    'Comparator1 Interrupt Handler+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++        
    Comp1_Int:
            if T1CON.0= 0 then 'if timer1 is not running...
            TMR1H = 0 'Set the high part of the timer value to 0
            TMR1L = 0 'Set the low part of the timer value to 0 
            T1CON.0= 1  'start timer
            else        'therefore if it is running, stop the timer & calculate the number of 'clock' counts between comparator interrupts....
            T1CON.0= 0  'stop the timer
            MyTime.Lowbyte = TMR1L 'puts the timer's low byte in MyTime's lower 8 bits 
            MyTime.Highbyte = TMR1H 'puts the timer's high byte in MyTime's upper 8 bits
            dummy = b * c       '5,000,000 
            frequency = div32 mytime ' this convertes to Hz, but with no decimal points
            HSEROUT [dec frequency/10,".",dec frequency//10,"Hz",13, 10]   .....this places a decimal point in the right place to make the reading easier on the eye.
            endif 
    @ INT_RETURN
    Note: for the comparator to have something to erhm 'compare' against, I've still not got the PIC's internal voltage reference to work yet, so I was feeding 1/2 VCC externally into the 16F690's RA0 Pin 19 (the bolded bit in this command sets this up.... CM1CON0 = %11100011 )
    Last edited by HankMcSpank; - 28th August 2010 at 17:40.

  11. #11
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default

    To get more resolution/accuracy for calculating phase, I really need to crank the OSC up to 20Mhz (ie use an ext oscillator).

    Ok, the lowest note on a guitar is about 82Hz (std tuning)

    Is my line of thinking correct here.?...

    One full cycle @82hz takes 0.012195122 seconds

    One PIC clock cycle @20Mhz is 0.000000125 seconds.

    therefore the number of clock cycles that will occure when a note of 82Hz is played will be 243,902.

    I'm using a 16F690, which has a 16 bit timer - a 16 bit timer will over flow at a count of 65536 - alas this number is smaller than 243,902.

    therefore to be able to count 82Hz at 20Mhz, I need a tmr overflow interrupt handler to increment a variable with each tmr overflow (ie count the number of tmr overflows). therefore at 82Hz signal and 20Mhz clock a 16 bit timer will over flow 3 times.

    So (you still awake at the back?), to get the true total number of PIC clock counts @ 82Hz, I need to multiply the stored tmr overflow (3) count by 65536 & then add in the existing tmr count number. chunky numbers in play for a device costing $2.

    Is the PIC gonna start struggling with the above...or will it take it in its stride? (is there a better route I should wrt all of this?!!)
    Last edited by HankMcSpank; - 8th September 2010 at 11:32.

Similar Threads

  1. Measuring change of frequency with PIC
    By Aussie in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 19th July 2007, 01:47
  2. PIC Audio
    By toofastdave in forum mel PIC BASIC Pro
    Replies: 28
    Last Post: - 27th June 2007, 13:49
  3. Pic driven digital audio delay
    By skimask in forum Off Topic
    Replies: 12
    Last Post: - 19th April 2007, 20:42
  4. Audio Encoding and playback in a PIC
    By Rob in forum mel PIC BASIC Pro
    Replies: 8
    Last Post: - 24th March 2005, 08:56
  5. Frequency Counter using PIC and PicBasic
    By PICtron in forum mel PIC BASIC Pro
    Replies: 31
    Last Post: - 28th January 2005, 06:20

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts