Math seems to not work

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I started a project on the Attiny 4313 and i need to convert midi notes to frequency. The formula is pretty straight forward but it does not work for some reason. I'm outputting the values to a YM2149f, which works fine with any frequency you input. I'm new to AVR and just left Arduino, so maybe it is just some data type problem.

Also, seems that at every A (440Hz, 880Hz, --->....) the code works.

Here is my code any way:

 

int MIDI_FORMULA (int note){
	 float freq = 0;
	 float temp = 0;
	 float result = 0;
	 
	 temp = ((note-69)/12);
	 
	 
	 freq = (pow(2,temp)*440);
	 
	 result = (unsigned int ) freq;
	 
	 return result;
}

 

 

The frequencies need to be rounded to the next integer

PS: The formula is 2^((note -69)/12)*440.

This topic has a solution.
Last Edited: Tue. Jul 21, 2020 - 11:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How are you sending to the MIDI device?

 

I don't know what format is used by MIDI, but one could be an ASCII string. This is what you would generate from a terminal.  BUT, since you don't say, you might be sending as binary.  Of course, these are not the only plausible formats. But, in this situation, they are probably at the top of the list.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi ka7ehk, thanks for the quick reply!

I didn't plug the MIDI input yet, I wrote an array with some notes and also a for loop going from 21 to 108:

 

int MIDI_NOTE_TEST (){

int note [] = {67,69,62,60,62,60};

	for (int i = 21; i < 108; i++){
		
		
		tone (1 , MIDI_FORMULA(i), 15);
		_delay_ms (50);
		notone(0);
		_delay_ms (50);
		
	}
	_delay_ms (1000);
	
	
}
		

I don't think it is this part of the code that is causing problems.  

 

It partially works with the A notes. 

 

Like, is there a problem with my formula function? Because once I had a similar issue on Arduino IDE and I just changed the data type to fix it. 

 

Here is another piece of code that woks fine with frequencies: 

 

    for (int i = 440; i<2000; i++){
	tone(1, i, 15);
			
	_delay_ms(10);
    }

The midi part will work with an FTDI and Hairless MIDI but it is not ready yet. 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

enzo1522 wrote:

	 temp = ((note-69)/12);
	 
	 
	 freq = (pow(2,temp)*440);
	 
	 result = (unsigned int ) freq;
	 

 

Try using

    temp = (( (float)note - 69.0)/12.0);

    ...

   result = trunc(freq);   // trunc() is in <math.h>

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just did the changes, before the alterations, some notes would play but now only 2 consecutive out of the 87 play.

I think I will create an array with the frequencies and try it.

 

Thanks for the reply Chuck!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A table might be a better solution. The float operations require a bit of code space and take significant time to execute. Besides, your input and output are integers, so the table solution will most likely be faster and take less code space.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 is there a problem with my formula function

Did you not look even at your formula?  Are these the values you are expecting? No coding until you have a formula you like!

Assuming these are the numbers desired, you can then implement some code to calc them, or maybe just put them in a lookup table.

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Tue. Jul 21, 2020 - 02:11 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

 

The internet tells me:

so I guess a test would be to use 48..72 and see if that works. I'd start by doing this on a PC:

#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>

int MIDI_FORMULA(int note) {
    float freq = 0;
    float temp = 0;
    float result = 0;

    temp = ((note - 69) / 12);

    freq = (pow(2, temp) * 440);

    result = (unsigned int)freq;

    return result;
}

int main(void) {
    for (int i = 48; i < 73; i++) {
        printf("# = %d, freq = %d\n", i, MIDI_FORMULA(i));
    }
}

when I run that I get:

# = 48, freq = 220
# = 49, freq = 220
# = 50, freq = 220
# = 51, freq = 220
# = 52, freq = 220
# = 53, freq = 220
# = 54, freq = 220
# = 55, freq = 220
# = 56, freq = 220
# = 57, freq = 220
# = 58, freq = 440
# = 59, freq = 440
# = 60, freq = 440
# = 61, freq = 440
# = 62, freq = 440
# = 63, freq = 440
# = 64, freq = 440
# = 65, freq = 440
# = 66, freq = 440
# = 67, freq = 440
# = 68, freq = 440
# = 69, freq = 440
# = 70, freq = 440
# = 71, freq = 440
# = 72, freq = 440

So I would put your AVR off to one side for the time being and start by exploring why the formula doesn't even appear to work on a PC. It's far more easy to test "generic logic" like this on a PC where you have easy access to things like printf() before you move things to the AVR. Also on a PC you can very easily debug the operation of the function to look at what is going on "inside".

 

BTW don't use all upper case names for a C function - it looks like a preprocessor macro name!

 

Oh and had it actually worked for "PC maths" where sizeof(int)==4 I was then going to suggest you explore what might happen on an AVR where sizeof(int)==2. You could simulate this on a PC with:

int16_t MIDI_formula(int16_t note) {
    float freq = 0;
    float temp = 0;
    float result = 0;

    temp = ((note - 69) / 12);

    freq = (pow(2, temp) * 440);

    result = (uint16_t)freq;

    return result;
}

Even if you only did that you would spot the fact that in this code you cast freq into result with "unsigned int" but the function return value was "int" so the sign did not match.

 

Anyway the issue is in:

    temp = ((note - 69) / 12);

remember a fundamental rule of C is that the thing on the left of = has no influence on the way the calculation on the right is performed. Just because "temp" is float (which does mean that once the calculation is finished the result will be changed to float for the assignment) it does not mean that the right side is actually calculated as float. So let's try a thought experiment where note=61. 61 - 69 is -8 and -8 / 12 (in integer maths!) is 0. I bet you intended this latter bit to be done in decimal fractions? If that's what you meant you need to say that "12" is "a floating point 12". You could do that as "(float)12" but a more usual way is to use a suffix and, even though it's a whole number a decimal point so "12.0F". With:

    temp = ((note - 69) / 12.0F);

I get:

# = 48, freq = 130
# = 49, freq = 138
# = 50, freq = 146
# = 51, freq = 155
# = 52, freq = 164
# = 53, freq = 174
# = 54, freq = 184
# = 55, freq = 195
# = 56, freq = 207
# = 57, freq = 220
# = 58, freq = 233
# = 59, freq = 246
# = 60, freq = 261
# = 61, freq = 277
# = 62, freq = 293
# = 63, freq = 311
# = 64, freq = 329
# = 65, freq = 349
# = 66, freq = 369
# = 67, freq = 391
# = 68, freq = 415
# = 69, freq = 440
# = 70, freq = 466
# = 71, freq = 493
# = 72, freq = 523

which seems in much closer agreement to that table I plucked from the internet.

 

BTW if I change my little test harness to be:

int main(void) {
    printf("size = %u\n", sizeof(int));
    for (int i = 0; i < 130; i++) {
        printf("# = %d, freq = %d\n", i, MIDI_FORMULA(i));
    }
    printf("uint16_t freq[] = { \n\t");
    for (int i = 0; i < 130; i++) {
        printf("%d", MIDI_FORMULA(i));
        if (i < 129) {
            printf(", ");
        }
        if ((i % 12) == 11) {
            printf("\n\t");
        }
    }
    printf("\n};\n");
}

then at the end of the test output I get:

uint16_t freq[] = {
        8, 8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 29, 30,
        32, 34, 36, 38, 41, 43, 46, 48, 51, 55, 58, 61,
        65, 69, 73, 77, 82, 87, 92, 97, 103, 110, 116, 123,
        130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
        261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493,
        523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987,
        1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975,
        2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951,
        4186, 4434, 4698, 4978, 5274, 5587, 5919, 6271, 6644, 7040, 7458, 7902,
        8372, 8869, 9397, 9956, 10548, 11175, 11839, 12543, 13289, 14080
};

Sure this table "eats" 260 bytes of precious memory but for my money I wonder if this might be a "better solution". Now given note=0..129 I would just use freq[note] to get the frequency with no runtime calculation (because that just happened on my PC!).

Last Edited: Tue. Jul 21, 2020 - 08:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I doubt this is the bug - but it is suspicious:

int MIDI_FORMULA (int note){
    float freq = 0;
    float temp = 0;
    float result = 0;

 

    temp = ((note-69)/12);

    freq = (pow(2,temp)*440);

 

    result = (unsigned int) freq;

    return result;

}

 

Last Edited: Tue. Jul 21, 2020 - 10:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yup...

clawson wrote:
Even if you only did that you would spot the fact that in this code you cast freq into result with "unsigned int" but the function return value was "int" so the sign did not match.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Sure this table "eats" 260 bytes of precious memory

Compare to the size of the needed FP routines.  It might be useful to note [pun intended] the difference in processor cycles.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Clawson, Thanks for the reply and the solution of the problem!

 

I didn't know about that aspect of integer maths, but sure I wasn't expecting that. I just left Arduino and now I'm leaning AVR by my self.

 

I was thinking about run that formula somewhere else to check if it was working. I did it on Desmos and it worked.

But now I know Atmel Studio can do the math. So thanks for that as well!

 

Well, I will just use the array, it does consume memory, but it is better. 

I will check the how float points work later to prevent that from happening again.

Thanks for the huge help!  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

enzo1522 wrote:
Well, I will just use the array, it does consume memory,
Talking of which it is "const" so no need for RAM. As such I'd make sure:

uint16_t freq[] = {

was actually:

const __flash uint16_t freq[] = {

so that it only resides in flash.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

enzo1522 wrote:
Well, I will just use the array, it does consume memory
theusch wrote:
Compare to the size of the needed FP routines

So, what is the balance?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You do realise that the underlying compiler is the same? The exact same code should perform identically whether you use Arduino or Atmel Studio.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is a bizarre formula and I don't believe that it works for this application.

 

A bit of review: 

   1/12 = 0.0833

   2 raised to the power of 0.0833 = 1.0594

   any note's frequency multiplied by 1.0594 equals the frequency of the next note on the scale:

    middle C =262 Hz ;    262  * 1.059 = 277 Hz  == C#

         C#  = 277 Hz;   277 * 1.0594 =  293Hz  == D    

 

...and so on for an "equally tempered" scale.   In this scale, all the notes are in tune for one key and one key only.  A "well tempered" scale has these frequencies slightly different so that, all the keys sound equally well, but each is slightly out-of-tune to the trained ear. European musicians wrestled with this in the 1500s and 1600s, but it was J.S.Bach's "A Well Tempered Klavier" in 1680? that led to the adoption of the well-tempered vs. equal tempered scale for widespread use. 

 

I suggest that you make a table of the values that the YM2149F uses for each MIDI note.   Then when you get a Note-On message for a particular note, go to the table, get the values for that note and write these values to the YM2149F channel, along with the data needed to turn the note on, or start the envelope generator.

Discard the complicated formulas that don't seem to work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
I don't believe that it works for this application.
But the numbers don't lie? When fixed it produced:

clawson wrote:

# = 48, freq = 130
# = 49, freq = 138
# = 50, freq = 146
# = 51, freq = 155
# = 52, freq = 164
# = 53, freq = 174
# = 54, freq = 184
# = 55, freq = 195
# = 56, freq = 207
# = 57, freq = 220
# = 58, freq = 233
# = 59, freq = 246
# = 60, freq = 261
# = 61, freq = 277
# = 62, freq = 293
# = 63, freq = 311
# = 64, freq = 329
# = 65, freq = 349
# = 66, freq = 369
# = 67, freq = 391
# = 68, freq = 415
# = 69, freq = 440
# = 70, freq = 466
# = 71, freq = 493
# = 72, freq = 523

which are the widely documented frequencies for MIDI note numbers (rounded to integer).

 

Sites that document MIDI note numbers/frequencies:

 

https://www.inspiredacoustics.com/en/MIDI_note_numbers_and_center_frequencies

https://www.audiolabs-erlangen.de/resources/MIR/FMP/C1/C1S3_CenterFrequencyMIDI.html

https://en.wikipedia.org/wiki/MIDI_tuning_standard#Frequency_values