Ad Widget

Collapse

Announcement

Collapse
No announcement yet.

Is anyone here an expert in PIC?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Ah....I'm 'in the zone' for a new design using Arduino - mainly because I now realize that it's possible to do bends, slides, vibrato etc by not quantizing the notes. Just thinking through the maths and I'm just not clear on the formula to convert frequency in Hz to a 1v/octave range.

    The output of an Arduino is zero to 5V, so with 1v/oct is 5 octaves, at 0.0833v per semitone. I found a formula of V=Log2(F/55) which seems to work out, but I'm unclear as to what the divide by 55 relates to - can anyone explain this?

    The voltages need to be as follows (taken from my existing quantized output which is in tune) - columns are DAC output, note number, voltage. Clearly bends, slides etc also need to be accommodated by the formula. For reference guitar low E, 82.41 Hz, is note #7 0.5833 V

    0, // 0 0,0000 V
    62, // 1 0,0833 V
    140, // 2 0,1667 V
    202, // 3 0,2500 V
    276, // 4 0,3333 V
    340, // 5 0,4167 V
    411, // 6 0,5000 V
    478, // 7 0,5833 V
    546, // 8 0,6667 V
    616, // 9 0,7500 V
    682, // 10 0,8333 V
    754, // 11 0,9167 V

    816, // 12 1,0000 V
    891, // 13 1,0833 V
    952, // 14 1,1667 V
    1027, // 15 1,2500 V
    1086, // 16 1,3333 V
    1162, // 17 1,4167 V
    1222, // 18 1,5000 V
    1297, // 19 1,5833 V
    1361, // 20 1,6667 V
    1434, // 21 1,7500 V
    1501, // 22 1,8333 V
    1569, // 23 1,9167 V

    1639, // 24 2,0000 V
    1705, // 25 2,0833 V
    1777, // 26 2,1667 V
    1840, // 27 2,2500 V
    1916, // 28 2,3333 V
    1977, // 29 2,4167 V
    2053, // 30 2,5000 V
    2112, // 31 2,5833 V
    2189, // 32 2,6667 V
    2252, // 33 2,7500 V
    2325, // 34 2,8333 V
    2390, // 35 2,9167 V

    2460, // 36 3,0000 V
    2527, // 37 3,0833 V
    2594, // 38 3,1667 V
    2665, // 39 3,2500 V
    2730, // 40 3,3333 V
    2803, // 41 3,4167 V
    2865, // 42 3,5000 V
    2941, // 43 3,5833 V
    3001, // 44 3,6667 V
    3076, // 45 3,7500 V
    3137, // 46 3,8333 V
    3213, // 47 3,9167 V

    3275, // 48 4,0000 V
    3349, // 49 4,0833 V
    3415, // 50 4,1667 V
    3485, // 51 4,2500 V
    3554, // 52 4,3333 V
    3621, // 53 4,4167 V
    3693, // 54 4,5000 V
    3758, // 55 4,5833 V
    3834, // 56 4,6667 V
    3895, // 57 4,7500 V
    3972, // 58 4,8333 V
    4031, // 59 4,9167 V
    4095 // 60 5,0000 V​

    Comment


    • #17
      Originally posted by Mick Bailey View Post

      The output of an Arduino is zero to 5V, so with 1v/oct is 5 octaves, at 0.0833v per semitone. I found a formula of V=Log2(F/55) which seems to work out, but I'm unclear as to what the divide by 55 relates to - can anyone explain this?
      I don't understand semitones but from formula I have seen I believe the 55 is related to the frequency of note A4 which is 440Hz.

      Comment


      • #18
        So you are outputting only those specific voltages or whatever voltage the formula dictates at any given time? Calculating logs can be slow and you can avoid that by creating lookup tables or doing the log "calculation" to the signal on the front-end.

        Comment


        • #19
          My thought was to output the instantaneous voltage calculated form the frequency. I hadn't appreciated the calculation time. A lookup table would mean quantizing the notes to a specific value, which would mean vibrato etc could not be applied. From my limited knowledge I was going to loop the frequency detection (calculated from adding the positive and zero waveform timings together) and the frequency-to-voltage conversion output to the DAC or PWM. I don't know then whether the log calculation would be noticeable. Maybe I should breadboard the circuit and write some code to see how it works. Maybe there's an Arduino reference that gives timings to mathematical processes.

          Comment


          • #20
            The voltage output by the DAC is not continuous, it is, I think, up to 10 bits of resolution on the Arduino. Another way of looking at it is to say that a table doesn't need any more entries than that or that you are always forced to quantize notes by that 10 bit restriction even if you calculate the log on the fly. It is a bit of a balancing act if you want to get the best possible performance. You need to test it to see where you could save time and what the cost in memory or processing cycles would be.

            Write the program with the log calculation in it, but keep in mind that you might want to replace it with a table in the next version if latency is an issue.

            Comment


            • #21
              Arduino Nano doesn't have an inbuilt DAC so I use a serial external 12 bit device - 5 octaves gives 4096 steps, or roughly 68 steps per semitone. With a capacitor on the DAC output to give a little slew I think it would sound fairly smooth with vibrato or bends. I'd need to try it. The other approach is to use Arduino's PWM which can use 14 bits if two outputs are combined using the Mozzi library. With an RC filter and buffer this may work. I need to do a bit more research on how to code this efficiently and give it a go.

              Comment


              • #22
                You have it well in hand. PM me if you want me to look at your coding later on. Out of all this stuff only programming is my thing.

                Comment


                • #23
                  Programming is difficult for me to get a grip on. I can learn enough for a specific task, then 6 months later its like starting again. Will post my code here when I get something together.

                  Comment


                  • #24
                    I don't think your lookup table would have to have 2^12 entries, I'd start by calculating the exact lookup for each semitone over 5 octaves and using a linear interpolation. Might be be close enough for bendin' and slidin'.

                    Filtering the output for slew allows you to control portamento as well. There must be a simple way to avoid slewing from zero to the voltage on every "note on" event, though, as the earliest analog synths could handle that... just thinking out loud.
                    If it still won't get loud enough, it's probably broken. - Steve Conner
                    If the thing works, stop fixing it. - Enzo
                    We need more chaos in music, in art... I'm here to make it. - Justin Thomas
                    MANY things in human experience can be easily differentiated, yet *impossible* to express as a measurement. - Juan Fahey

                    Comment


                    • #25
                      The setup I've got a the moment does use a semitone lookup table - 60 values over 5 octaves. It can do pseudo bends (though 'steppy'), hammer-ons and slides by altering the note detection algorithm using a switch on the front panel. I also built in a portamento control which at zero on the pot disables it and at maximum takes about three seconds to slew from an E played on the 12th fret of the top string to bottom E. It all works quite well, apart from the latency on the lower strings, but is a bit unnatural from a guitar perspective as finger vibrato is eliminated and blues bends are quantized to the nearest lookup value. There is one benefit in that the output is always spot-on in tune.

                      The DAC output calculation looks like it just needs to be DACout=V/5*4095 where V is the voltage calculated from the frequency. I'm not clear right now on the best way to convert the floating point output from the DAC calculation to the closest integer value for the DAC, which will only accept an integer between 0 and 4095 (anything else is ignored). There seems to be a number of ways to do this.

                      Comment


                      • #26
                        What is your goal now that you are moving on from the old setup? Do you wish to take full advantage of the DAC's 4K resolution? Is the identification of notes/semitones important? One feature could be dynamically loading/choosing tables of varying sizes and composition.

                        Take a look at how the math library is implementing the functions you are planning to use.

                        How fast is your serial external DAC?

                        Comment


                        • #27
                          For the new setup I want a much quicker note detection and the ability to give a continuously variable output voltage that isn't quantized to semitone intervals. I like your idea of loading a lookup table - I could have a switch to choose between either function. The external DAC is an MCP4725 with a maximum transfer speed of 3.4 MHz.

                          My first step is to get the frequency detection and output to DAC working and see how much resource this takes when compiled, then I can work out what else I can do with the remaining space (assuming the basic functionality is fast enough). I need a good rainy day as an excuse to sit at the PC and do nothing else.

                          Comment


                          • #28
                            I just read some interesting stuff from a variety of sources about making frequency counters using the Arduino pulseIn function and a Schmitt trigger. (This is my morning coffee reading...lol)

                            Since your DAC has 12 bit resolution you could have a table with 4K entries mapping frequency to voltage. You would never need to calculate. Since you have so many Nanos you could devote one to doing table "stuff" if you are memory constrained.

                            Comment


                            • #29
                              Yes, the pulseIn function is what I've chosen to use and the squaring function after my fundamental extractor is a Schmitt trigger which means there's no delay prior to the Arduino, which should get the frequency read down to an acceptable level. I also intend to generate the trigger and gate as a side chain pre-Arduino using an envelope detector.

                              EDIT: Just did a quick and simple bit of code and it compiles with no errors. I have some uncertainty with the line "dac_value = mv_conversion / 5000 * 4095; // get DAC value as an integer" dac_value is an integer and mv_conversion is floating point. Does this line effectively convert the float to int? I want the calculation to be done as floating point, but store the value as an integer (losing the resolution in the process doesn't matter).

                              Frequency_to_CV_1.0.txt
                              Attached Files
                              Last edited by Mick Bailey; 08-13-2023, 02:27 PM.

                              Comment


                              • #30
                                Originally posted by Mick Bailey View Post
                                Yes, the pulseIn function is what I've chosen to use and the squaring function after my fundamental extractor is a Schmitt trigger which means there's no delay prior to the Arduino, which should get the frequency read down to an acceptable level. I also intend to generate the trigger and gate as a side chain pre-Arduino using an envelope detector.

                                EDIT: Just did a quick and simple bit of code and it compiles with no errors. I have some uncertainty with the line "dac_value = mv_conversion / 5000 * 4095; // get DAC value as an integer" dac_value is an integer and mv_conversion is floating point. Does this line effectively convert the float to int? I want the calculation to be done as floating point, but store the value as an integer (losing the resolution in the process doesn't matter).

                                Frequency_to_CV_1.0.txt
                                In C you can get away with a lot of bad habits...until you get caught. I haven't checked in detail, but there seem to be a lot of implicit casts here. An implicit cast is done when the compiler detects a mismatch between data types. This could occur during assignment or a function call etc. In the context of what you are doing it probably doesn't matter, but it is stuff like this that can bite you in the butt. Be aware what data type the function returns and what types the function parameters are.

                                Off hand if you are having some troubles I would say it is with (mv_conversion / 5000). Since dac_value is int, I think the compiler will "helpfully" force this to an int before multiplying by 4095. This implicit cast will lose all the precision. You have some of what I call "magic numbers" in your code. 55, 1000, 5000, 4095. Make those constants. It makes the code more readable and will put those values up front so you can easily modify them if necessary.

                                An optimization would be to examine the calculations you are doing. I think that the 1000, 5000 and 4095 can all be boiled down to a single float. This can be done manually or you can make the compiler do it.

                                I don't know how Arduino programmers test stuff when there is no console to print to. If you are debugging you could do something like connect LEDs to output pins and put a delay in your code so you can turn these LED on or off according to tests you do in your code. For example you could test to see if the dac_value is as you expected when you input a specific note and let the LEDs tell you.

                                I am thinking of getting a Teensy to play with.

                                How big is your compiled code?
                                Last edited by Pixel; 08-13-2023, 05:37 PM.

                                Comment

                                Working...
                                X