// Convert a block from PCM to BRR // Returns the squared error between original data and encoded data // If "is_end_point" is true, the predictions p1/p2 at loop are also used in caluclating the error (depending on filter at loop) private static double ADPCMMash(int shiftamount, byte filter, Int16[] PCMData, bool write, bool isEndPoint, BrrEncoderOptions options, BrrEncoderStatistics statistics, ref Int16 p1, ref Int16 p2, ref byte[] brr) { double d2 = 0.0; Int16 l1 = p1; Int16 l2 = p2; int step = 1 << shiftamount; int vlin, d, da, dp, c; if (PCMData.Length < 16) { return(0); } for (int i = 0; i < 16; ++i) { //Make linear prediction for next sample //vlin = (v0 * iCoef[0] + v1 * iCoef[1]) >> 8; vlin = getBrrPrediction(filter, l1, l2) >> 1; d = (PCMData[i] >> 1) - vlin; //difference between linear prediction and current sample da = Math.Abs(d); // Take advantage of wrapping if (options.wrapEn && da > 16384 && da < 32768) { d = d - 32768 * (d >> 24); //if(write) printf("Caution : Wrapping was used.\n"); } dp = d + (step << 2) + (step >> 2); c = 0; if (dp > 0) { if (step > 1) { c = dp / (step / 2); } else { c = dp * 2; } if (c > 15) { c = 15; } } c -= 8; dp = (c << shiftamount) >> 1; // quantized estimate of samp - vlin // edge case, if caller even wants to use it if (shiftamount > 12) { dp = (dp >> 14) & ~0x7FF; } c &= 0x0f; // mask to 4 bits l2 = l1; //shift history */ l1 = (Int16)(sclamp16(vlin + dp) * 2); d = PCMData[i] - l1; d2 += (double)d * d; // update square-error if (write) // If we want output, put it in proper place { brr[1 + (i >> 1)] |= ((i & 1) != 0) ? (byte)c : (byte)(c << 4); } } // Also account for history points when looping is enabled & filters used if (!isEndPoint) { d2 /= 16.0; } else { switch (statistics.filterAtLoop) { case 0: d2 /= 16.0; break; case 1: // Filter 1 d = l1 - statistics.p1AtLoop; d2 += (double)d * d; d2 /= 17.0; break; default: // Filters 2 & 3 d = l1 - statistics.p1AtLoop; d2 += (double)d * d; d = l2 - statistics.p2AtLoop; d2 += (double)d * d; d2 /= 18.0; break; } } // when generating real output, we want to return these if (write) { p1 = l1; p2 = l2; //Set the end bit if we're on the last block brr[0] = Convert.ToByte((shiftamount << 4) | (filter << 2)); if (isEndPoint) { brr[0] |= 1; } } return(d2); }
// Encode a ADPCM block using brute force over filters and shift amounts private static void ADPCMBlockMash(Int16[] PCMData, bool isLoopPoint, bool isEndPoint, BrrEncoderOptions options, BrrEncoderStatistics statistics, ref Int16 p1, ref Int16 p2, ref byte[] brr) { byte smin = byte.MaxValue; byte kmin = byte.MaxValue; double dmin = double.MaxValue; for (byte s = 0; s < 13; ++s) { for (byte k = 0; k < 4; ++k) { if (!options.FIRen[k]) { continue; } double d = ADPCMMash(s, k, PCMData, false, isEndPoint, options, statistics, ref p1, ref p2, ref brr); if (d >= dmin) { continue; } kmin = k; //Memorize the filter, shift values with smaller error dmin = d; smin = s; } } if (isLoopPoint) { statistics.filterAtLoop = kmin; statistics.p1AtLoop = p1; statistics.p2AtLoop = p2; } ADPCMMash(smin, kmin, PCMData, true, isEndPoint, options, statistics, ref p1, ref p2, ref brr); statistics.FIRstats[kmin]++; }
public static byte[] encodeBrr(WavFileHeader hdr, Int16[] samples, BrrEncoderOptions options) { List <byte> output = new List <byte>(); byte loopFlag = 0x00; UInt16 loopStart = 0; uint newLoopsize = 0; if (options.loopAddress >= 0) { loopFlag = 0x02; // 0x02 if loop flag is active. loopStart = Convert.ToUInt16(options.loopAddress); } // Optional truncation of input sample UInt32 samplesLength = hdr.dataSize / hdr.blockAlign; // FIXME comprobar qué es esto ¿no se puede utilizar samples.Length? // Adjust amplitude in function of amount of channels samples = adjustAmplitudeOfWavSamples(samples, samplesLength, hdr.chans, hdr.bitsPerSample, options); // Apply resampling if needed applyResampling(ref samples, ref samplesLength, ref newLoopsize, hdr.sampleRate, loopStart, options); // Apply trebble boost filter (gussian lowpass compensation) if requested by user if (options.trebleBoost) { samples = trebleBoostFilter(samples, samplesLength); } //Add zeroes at the beginning if the Amount of PCM samples isn't a multiple of 16 int zeroesToAdd = samples.Length % 16; if (zeroesToAdd != 0) { samples = Enumerable.Repeat((Int16)0, zeroesToAdd).Concat(samples).ToArray(); } //Begin the actual BRR encoding //Initialization needed if any of the first 16 samples isn't zero bool needsInitialBrrBlock = false; if (samples.Length >= 16) { for (int i = 0; i < 16 && !needsInitialBrrBlock; ++i) { needsInitialBrrBlock |= samples[i] != 0; } } else { needsInitialBrrBlock = true; } if (needsInitialBrrBlock) { output = new List <byte>() { loopFlag, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } ; //Encode BRR //Set the truncate size if (options.truncateLen != 0 && output.Count > 0) { options.truncateLen -= 16; } if (options.truncateLen != 0 && (options.truncateLen != samplesLength)) { samplesLength = options.truncateLen; } Int16 p1 = 0; Int16 p2 = 0; BrrEncoderStatistics statistics = new BrrEncoderStatistics(); for (int n = 0; n < samplesLength; n += 16) { byte[] brrChunk = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //Encode BRR block, tell the encoder if we're at loop point (if loop is enabled), and if we're at end point bool isLoopPoint = options.fixLoopEn && (n == (samplesLength - newLoopsize)); bool isEndPoint = n == samplesLength - 16; Int16[] samplesToCheck = samples.Skip(n).ToArray(); if (samplesToCheck.Length < 16) { samplesToCheck = samplesToCheck.Concat(new Int16[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }).ToArray(); } ADPCMBlockMash(samples.Skip(n).ToArray(), isLoopPoint, isEndPoint, options, statistics, ref p1, ref p2, ref brrChunk); //Set the loop flag if needed brrChunk[0] |= loopFlag; output.AddRange(brrChunk); } //HACKME recover this // if(fix_loop_en) // { // unsigned int k = samples_length - (initial_block ? new_loopsize - 16 : new_loopsize); // printf("Position of the loop within the BRR sample : %u samples = %u BRR blocks.\n", k, k/16); // } // for(int i=0; i<4; i++) // if (FIRstats[i]>0) printf("Filter %u used on %u blocks.\n", i, FIRstats[i]); return(output.ToArray()); }