Draft
At the moment, there are in the code these two parts which penalizes players if they make a miss. These misses are not scaled so longer maps got much lower pp count plays in average.
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
_aimValue *= pow(0.97f, _numMiss);
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
_speedValue *= pow(0.97f, _numMiss);
This could be simply fixed by multiplying the power by numTotalHits/576 ratio as shown here. By this, penalization would be scaled according to the hitobject count.
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
_aimValue *= pow(0.97f, _numMiss*(numTotalHits/576));
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
_speedValue *= pow(0.97f, _numMiss*(numTotalHits/576));
Why 576
Average map in osu! is a 90 - 120 seconds long. These maps have mostly 550 - 600 hitobjects so mean is 575 so the 576 is pretty close. In my opinion if average hitobject count would be counted, 576 would be closer than 575.
576 is 1001000000 in binary which makes it faster for computers to work with (it has only one '1' digit in the number if we don't count the first digit) so the computation won't take much longer.
Edit
I think that numTotalHits is count of hitobjects. Correct me if I am wrong. I also forgot that numTotalHits nor 576 is not float so it will have to be written as numTotalHits/576f but you got the point.