Is there a faster way to do this? (map)

I have a loop with a few map functions in it, i was wondering if there is a clever way to reduce the time it takes to do these. (i hope this makes sense to someone)

map's have a divide and a multiply in them, and
I know neither of those are good for speed :-[ I had to use them here, i couldnt think of anything else that would keep a nice smooth output.

 bitClear (PORTG ,1); //Pin 40 test output for loop frequency and how much time code section takes by observing duty cycle on scope

  modmatrixDest[0] = 
    ((((map(LFO0F0,0,127,0,modmatrixSource[0]))>>10)
    + ((map (EG0F0,0,127,0,modmatrixSource[2]))>>11)
    + ((map (EG1F0,0,127,0,modmatrixSource[3]))>>11)
    + ((modmatrixSource[4] * VELF0)>>9)
    + ((map(NOTEF0,0,127,0,modmatrixSource[6]))>>10)    
    + filterfreq));
  modmatrixDest[0] = constrain(modmatrixDest[0],0,1023);
  
  modmatrixDest[1] =    
    ((map (CCuse[10],0,127,0,modmatrixSource[0]))
    + (map (CCuse[8],0,127,0,modmatrixSource[2]))
    + (map (CCuse[9],0,127,0,modmatrixSource[3]))        
    + ((modmatrixSource[4] * CCuse[11]))     
    );
    
  bitSet (PORTG ,1); //PIN40 test output high 
  UpdateOscs();

modmatrixSource and modmatrixDest are long integers

modmatrixSource[] are continuously changing, they range from -4,194,304 to 4,194,304 (i can use different or positive numbers only if it will help to make it faster, but no less than 1,048,576)

modmatrixDest[] are used by an ISR (osc frequency) and a PWM output (filter control voltage),

The other controls (CCuse, and LFO0F0 etc) are values from MIDI inputs and analog potentiometer inputs. they are bytes. and range from 0 to 127

I would like to add another set of modulation controls now i have set it up with external flash ROM, which would modulate the position in a wavetable. but
Right now that bit of code is taking up half of my main loop, which is quite large, it contains MIDI input, analog input, MUX control, control scaling , 3 envelope generators and an LFO

the main loop frequency is at 650 hz, and it can not go below 500hz before you start noticing bad timing. (its a synthesizer btw)

any help would be greatly appreciated!

Let's go after the easy stuff first. Is anything in the code you posted declared float or double?

I have no floats or doubles in there at all, I have used long integers when i need a bit of precision, and everything else is int or bytes.

Excellent.

Next question. Have you determined for certain that the code above is causing the most performance problems?

yes, i have set and cleared a pin before and after (as shown in the code) and looked on the scope, the duty cycle is about 50% showing that this part of the code us using 50% or so of the time of the main loop.

remember if it seems a bit slow, the ISR uses about 30% of the cpu time

(setting and clearing the pins use hardly any time, 20mhz scope barely registers them if you put them right after each other)

Try this instead of map...

inline long DeFexMap( long n, long d )
{
  return( (n << 7) / (d+1) );
}

It should give slightly better results and the multiply is eliminated. If the compiler does actually inline the code, a call and return are eliminated.

Unfortunately, I can't see any other simple things to try.

Thanks!
that almost worked, and it gave me an important step in the right direction!

what i ended up with was this. first i increased the maximum value of the modulators to get more resolution on the lowest setting(its a global constant so easy) then

inline long ModMap( long n, long d )
{
  return( (n>>7) * (d) );
}

and i found out multiplication is a lot faster than division in this case.

(over 1 khz now, with just with those 7 maps
changed, and the measurement pulse for that part is only low about 20% of the loop now ).

The resolution seems to be the same as it was before as well.

Thanks again! :slight_smile:

Can't you factor; add all the values (perhaps individually scaled) and then do the MAP on the result?

modmatrixDest[0] =
((((map(LFO0F0,0,127,0,modmatrixSource[0]))>>10)

  • ((map (EG0F0,0,127,0,modmatrixSource[2]))>>11)
  • ((map (EG1F0,0,127,0,modmatrixSource[3]))>>11)
  • ((modmatrixSource[4] * VELF0)>>9)
  • ((map(NOTEF0,0,127,0,modmatrixSource[6]))>>10)
  • filterfreq));

Well, not sure but I think your coding is not really efficient.

  1. Why are you coding modMatrixSource with 22 bits if you only take the 12 highter ones?
  2. Mapping half bytes (0 - 127) values to a larger space will never give you more values.

So finally, you have values coded on 22 bits but only use 127 of them. Perhaps you can express modmatrixDest in a different way.
Retrieving long values takes a significant time so if you can use byte or int.
Use integer arithmetics as it is faster than floatting point.
And as the atMega has a wired multiplier, it is faster to multiply than to divide

That controller modmatrixDest[0] is running a PWM output so it gets reduced. the modmatrixDest[1] uses all of its resolution in a phase accumulator oscillator. I want a common standard for all source so i can control anything with anything else and get a predictable result.

If i use ints on the oscillator it is glitchy and i can hear the individual steps when the pitch glides.

more accurate version. the modmatrixSource is limited to 16777216. but that is plenty.

I also like to start with a higher accuracy during all the calculations because when modulations are added or subtracted, the result is more accurate if they are bit reduced after the final calculation, even if only being used for a 8 bit pwm signal. and the phase accumulator hardly has its bit depth reduced at all.
The multiplication and bit reducing is much faster then map. i had a few other maps which could be swapped for this as well, now the loop is about 2.5 faster then when i started this, and i have even added a third set of modulations.

inline long ModMap( long n, long d )
{
  return( (n*d)>>7 );
}

Perhaps useing float or double,I am not fully understand,what is that?

Are you suggestion that float or double should be used by DeFex?

Or do you need help understanding what a float or double is?