/* Sect D.1 Step4b A tone within the range (start -> end), must be 7.0 dB greater than all it's neighbours within +/- srange. Don't count its immediate neighbours. */ void psycho_3_tonal_label_range(FLOAT *power, int *tonelabel, int *maxima, FLOAT *Xtm, int start, int end, int srange) { int j,k; for (k=start;k<end;k++) /* Search for all the maxima in this range */ if (maxima[k] == 1) { tonelabel[k] = TONE; /* assume it's a TONE and then prove otherwise */ for (j=-srange;j<=+srange;j++) /* Check the neighbours within +/- srange */ if (abs(j) > 1) /* Don't count the immediate neighbours, or itself */ if ((power[k] - power[k+j]) < 7.0) tonelabel[k] = 0; /* Not greater by 7dB, therefore not a tone */ if (tonelabel[k] == TONE) { /* Calculate the sound pressure level for this tone by summing the adjacent spectral lines Xtm[k] = 10 * log10( pow(10.0, 0.1*power[k-1]) + pow(10.0, 0.1*power[k]) + pow(10.0, 0.1*power[k+1]) ); */ double temp = psycho_3_add_db(power[k-1], power[k]); Xtm[k] = psycho_3_add_db(temp, power[k+1]); /* *ALL* spectral lines within +/- srange are set to -inf dB So that when we do the noise calculate, they are not counted */ for (j=-srange;j<=+srange;j++) power[k+j] = DBMIN; } } }
/* D.1 Step 4.c Labelling non-tonal (noise) components Sum the energies in each critical band (the tone energies have been removed during the tone labelling). Find the "geometric mean" of these energies - i.e. find the best spot to put the sum of energies within this critical band. */ static void psycho_3_noise_label(psycho_3_mem * mem, FLOAT power[HBLKSIZE], FLOAT energy[BLKSIZE], int *tonelabel, int *noiselabel, FLOAT Xnm[HBLKSIZE]) { int i, j; int cbands = mem->cbands; int *cbandindex = mem->cbandindex; Xnm[0] = DBMIN; for (i = 0; i < cbands; i++) { /* for each critical band */ FLOAT sum = DBMIN; FLOAT esum = 0; FLOAT centreweight = 0; int centre; for (j = cbandindex[i]; j < cbandindex[i + 1]; j++) { Xnm[j] = DBMIN; /* go through all the spectral lines within the critical band, adding the energies. The tone energies have already been removed */ if (power[j] != DBMIN) { /* Found a noise energy, add it to the sum */ sum = psycho_3_add_db(mem, power[j], sum); /* calculations for the geometric mean FIXME MFC Feb 2003: Would it just be easier to do the *whole* of psycho_1 in the energy domain rather than in the dB domain? FIXME: This is just a lazy arsed arithmetic mean. Don't know if it's really going to make that much difference */ esum += energy[j]; /* Calculate the sum of energies */ centreweight += (j - cbandindex[i]) * energy[j]; /* And the energy moment */ } } /* MEANX, crash on AMD64 without this hack. See https://sourceforge.net/tracker/?func=detail&atid=735435&aid=1453400&group_id=136040 Probably a better way to do this */ if (sum <= DBMIN || esum < 0.00001) /* If the energy sum is really small, just pretend the noise occurs in the centre frequency line */ centre = (cbandindex[i] + cbandindex[i + 1]) / 2; else { /* Otherwise, work out the mean position of the noise, and put it there. */ centre = cbandindex[i] + (int) (centreweight / esum); } // /MEANX Xnm[centre] = sum; noiselabel[centre] = NOISE; } }
/* D.1 Step 4.c Labelling non-tonal (noise) components Sum the energies in each critical band (the tone energies have been removed during the tone labelling). Find the "geometric mean" of these energies - i.e. find the best spot to put the sum of energies within this critical band. */ void psycho_3_noise_label (FLOAT power[HBLKSIZE], FLOAT energy[BLKSIZE], int *tonelabel, int *noiselabel, FLOAT Xnm[HBLKSIZE]) { int i,j; Xnm[0] = DBMIN; for (i=0;i<cbands;i++) { /* for each critical band */ double sum = DBMIN; double esum=0; double centreweight = 0; int centre; for (j=cbandindex[i]; j<cbandindex[i+1]; j++) { Xnm[j] = DBMIN; /* go through all the spectral lines within the critical band, adding the energies. The tone energies have already been removed */ if (power[j] != DBMIN) { /* Found a noise energy, add it to the sum */ sum = psycho_3_add_db(power[j], sum); /* calculations for the geometric mean FIXME MFC Feb 2003: Would it just be easier to do the *whole* of psycho_1 in the energy domain rather than in the dB domain? FIXME: This is just a lazy arsed arithmetic mean. Don't know if it's really going to make that much difference */ esum += energy[j]; /* Calculate the sum of energies */ centreweight += (j - cbandindex[i]) * energy[j]; /* And the energy moment */ } } if (sum<=DBMIN) /* If the energy sum is really small, just pretend the noise occurs in the centre frequency line */ centre = (cbandindex[i] + cbandindex[i+1])/2; else /* Otherwise, work out the mean position of the noise, and put it there. */ centre = cbandindex[i] + (int)(centreweight/esum); Xnm[centre] = sum; noiselabel[centre] = NOISE; } }
/* ISO11172 Sect D.1 Step 6 Calculation of individual masking thresholds Work out how each of the tones&noises maskes other frequencies NOTE: Only a subset of other frequencies is checked. According to the standard different subbands are subsampled to different amounts. See psycho_3_init and freq_subset */ void psycho_3_threshold(FLOAT *LTg, int *tonelabel, FLOAT *Xtm, int *noiselabel, FLOAT *Xnm, FLOAT *bark, FLOAT *ath, int bit_rate, int *freq_subset) { int i,j,k; FLOAT LTtm[SUBSIZE]; FLOAT LTnm[SUBSIZE]; for (i=0;i<SUBSIZE;i++) { LTtm[i] = DBMIN; LTnm[i] = DBMIN; } /* Loop over the entire spectrum and find every noise and tone And then with each noise/tone work out how it masks the spectral lines around it */ for (k=1;k<HBLKSIZE;k++) { /* Find every tone */ if (tonelabel[k]==TONE) { for (j=0;j<SUBSIZE;j++) { /* figure out how it masks the levels around it */ FLOAT dz = bark[freq_subset[j]] - bark[k]; if (dz >= -3.0 && dz < 8.0) { FLOAT vf; FLOAT av = -1.525 - 0.275 * bark[k] - 4.5 + Xtm[k]; /* masking function for lower & upper slopes */ if (dz < -1) vf = 17 * (dz + 1) - (0.4 * Xtm[k] + 6); else if (dz < 0) vf = (0.4 * Xtm[k] + 6) * dz; else if (dz < 1) vf = (-17 * dz); else vf = -(dz - 1) * (17 - 0.15 * Xtm[k]) - 17; LTtm[j] = psycho_3_add_db (LTtm[j], av + vf); } } } /* find every noise label */ if (noiselabel[k]==NOISE) { for (j=0;j<SUBSIZE;j++) { /* figure out how it masks the levels around it */ FLOAT dz = bark[freq_subset[j]] - bark[k]; if (dz >= -3.0 && dz < 8.0) { FLOAT vf; FLOAT av = -1.525 - 0.175 * bark[k] - 0.5 + Xnm[k]; /* masking function for lower & upper slopes */ if (dz < -1) vf = 17 * (dz + 1) - (0.4 * Xnm[k] + 6); else if (dz < 0) vf = (0.4 * Xnm[k] + 6) * dz; else if (dz < 1) vf = (-17 * dz); else vf = -(dz - 1) * (17 - 0.15 * Xnm[k]) - 17; LTnm[j] = psycho_3_add_db (LTnm[j], av + vf); } } } } /* ISO11172 D.1 Step 7 Calculate the global masking threhold */ for (i=0;i<SUBSIZE;i++) { LTg[i] = psycho_3_add_db(LTnm[i], LTtm[i]); if (bit_rate < 96) LTg[i] = psycho_3_add_db(ath[freq_subset[i]], LTg[i]); else LTg[i] = psycho_3_add_db(ath[freq_subset[i]]-12.0, LTg[i]); } }