Loading services/audioflinger/AudioResamplerDyn.cpp +38 −17 Original line number Diff line number Diff line Loading @@ -161,7 +161,8 @@ void AudioResamplerDyn::Constants::set( AudioResamplerDyn::AudioResamplerDyn(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality) : AudioResampler(bitDepth, inChannelCount, sampleRate, quality), mResampleType(0), mFilterSampleRate(0), mCoefBuffer(NULL) mResampleType(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY), mCoefBuffer(NULL) { mVolumeSimd[0] = mVolumeSimd[1] = 0; mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better Loading Loading @@ -213,19 +214,16 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, double stopBandAtten, // test the filter and report results double fp = (fcr - tbw/2)/c.mL; double fs = (fcr + tbw/2)/c.mL; double fmin, fmax; testFir(buf, c.mL, c.mHalfNumCoefs, 0., fp, 100, fmin, fmax); double d1 = (fmax - fmin)/2.; double ap = -20.*log10(1. - d1); // passband ripple printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, (fmax + fmin)/2., d1, ap); testFir(buf, c.mL, c.mHalfNumCoefs, fs, 0.5, 100, fmin, fmax); double d2 = fmax; double as = -20.*log10(d2); // stopband attenuation printf("stopband(%lf, %lf): %.8lf %.8lf %.3lf\n", fs, 0.5, (fmax + fmin)/2., d2, as); double passMin, passMax, passRipple; double stopMax, stopRipple; testFir(buf, c.mL, c.mHalfNumCoefs, fp, fs, /*passSteps*/ 1000, /*stopSteps*/ 100000, passMin, passMax, passRipple, stopMax, stopRipple); printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, passMin, passMax, passRipple); printf("stopband(%lf, %lf): %.8lf %.3lf\n", fs, 0.5, stopMax, stopRipple); #endif } // recursive gcd (TODO: verify tail recursion elimination should make this iterate) // recursive gcd. Using objdump, it appears the tail recursion is converted to a while loop. static int gcd(int n, int m) { if (m == 0) { return n; Loading @@ -233,7 +231,16 @@ static int gcd(int n, int m) { return gcd(m, n % m); } static bool isClose(int32_t newSampleRate, int32_t prevSampleRate, int32_t filterSampleRate) { static bool isClose(int32_t newSampleRate, int32_t prevSampleRate, int32_t filterSampleRate, int32_t outSampleRate) { // different upsampling ratios do not need a filter change. if (filterSampleRate != 0 && filterSampleRate < outSampleRate && newSampleRate < outSampleRate) return true; // check design criteria again if downsampling is detected. int pdiff = absdiff(newSampleRate, prevSampleRate); int adiff = absdiff(newSampleRate, filterSampleRate); Loading @@ -255,8 +262,10 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { // TODO: Add precalculated Equiripple filters if (!isClose(inSampleRate, oldSampleRate, mFilterSampleRate)) { if (mFilterQuality != getQuality() || !isClose(inSampleRate, oldSampleRate, mFilterSampleRate, mSampleRate)) { mFilterSampleRate = inSampleRate; mFilterQuality = getQuality(); // Begin Kaiser Filter computation // Loading @@ -270,12 +279,12 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { double stopBandAtten; double tbwCheat = 1.; // how much we "cheat" into aliasing int halfLength; if (getQuality() == DYN_HIGH_QUALITY) { if (mFilterQuality == DYN_HIGH_QUALITY) { // 32b coefficients, 64 length useS32 = true; stopBandAtten = 98.; halfLength = 32; } else if (getQuality() == DYN_LOW_QUALITY) { } else if (mFilterQuality == DYN_LOW_QUALITY) { // 16b coefficients, 16-32 length useS32 = false; stopBandAtten = 80.; Loading @@ -289,8 +298,9 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { } else { tbwCheat = 1.03; } } else { // medium quality } else { // DYN_MED_QUALITY // 16b coefficients, 32-64 length // note: > 64 length filters with 16b coefs can have quantization noise problems useS32 = false; stopBandAtten = 84.; if (mSampleRate >= inSampleRate * 4) { Loading @@ -315,9 +325,20 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { int phases = mSampleRate / gcd(mSampleRate, inSampleRate); while (phases<63) { // too few phases, allow room for interpolation // TODO: Once dynamic sample rate change is an option, the code below // should be modified to execute only when dynamic sample rate change is enabled. // // as above, #phases less than 63 is too few phases for accurate linear interpolation. // we increase the phases to compensate, but more phases means more memory per // filter and more time to compute the filter. // // if we know that the filter will be used for dynamic sample rate changes, // that would allow us skip this part for fixed sample rate resamplers. // while (phases<63) { phases *= 2; // this code only needed to support dynamic rate changes } if (phases>=256) { // too many phases, always interpolate phases = 127; } Loading services/audioflinger/AudioResamplerDyn.h +2 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,8 @@ private: Constants mConstants; // current set of coefficient parameters int32_t __attribute__ ((aligned (8))) mVolumeSimd[2]; int32_t mResampleType; // contains the resample type. int32_t mFilterSampleRate; // designed sample rate for the filter int32_t mFilterSampleRate; // designed filter sample rate. src_quality mFilterQuality; // designed filter quality. void* mCoefBuffer; // if a filter is created, this is not null }; Loading services/audioflinger/AudioResamplerFirGen.h +428 −51 File changed.Preview size limit exceeded, changes collapsed. Show changes services/audioflinger/test-resample.cpp +118 −21 Original line number Diff line number Diff line Loading @@ -57,7 +57,8 @@ static int usage(const char* name) { int main(int argc, char* argv[]) { const char* const progname = argv[0]; bool profiling = false; bool profileResample = false; bool profileFilter = false; bool writeHeader = false; int channels = 1; int input_freq = 0; Loading @@ -65,10 +66,13 @@ int main(int argc, char* argv[]) { AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY; int ch; while ((ch = getopt(argc, argv, "phvsq:i:o:")) != -1) { while ((ch = getopt(argc, argv, "pfhvsq:i:o:")) != -1) { switch (ch) { case 'p': profiling = true; profileResample = true; break; case 'f': profileFilter = true; break; case 'h': writeHeader = true; Loading Loading @@ -230,27 +234,108 @@ int main(int argc, char* argv[]) { size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq; output_size &= ~7; // always stereo, 32-bits if (profileFilter) { // Check how fast sample rate changes are that require filter changes. // The delta sample rate changes must indicate a downsampling ratio, // and must be larger than 10% changes. // // On fast devices, filters should be generated between 0.1ms - 1ms. // (single threaded). AudioResampler* resampler = AudioResampler::create(16, channels, 8000, quality); int looplimit = 100; timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < looplimit; ++i) { resampler->setSampleRate(9000); resampler->setSampleRate(12000); resampler->setSampleRate(20000); resampler->setSampleRate(30000); } clock_gettime(CLOCK_MONOTONIC, &end); int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; int64_t time = end_ns - start_ns; printf("%.2f sample rate changes with filter calculation/sec\n", looplimit * 4 / (time / 1e9)); // Check how fast sample rate changes are without filter changes. // This should be very fast, probably 0.1us - 1us per sample rate // change. resampler->setSampleRate(1000); looplimit = 1000; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < looplimit; ++i) { resampler->setSampleRate(1000+i); } clock_gettime(CLOCK_MONOTONIC, &end); start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; time = end_ns - start_ns; printf("%.2f sample rate changes without filter calculation/sec\n", looplimit / (time / 1e9)); resampler->reset(); delete resampler; } void* output_vaddr = malloc(output_size); AudioResampler* resampler = AudioResampler::create(16, channels, output_freq, quality); size_t out_frames = output_size/8; /* set volume precision to 12 bits, so the volume scale is 1<<12. * This means the "integer" part fits in the Q19.12 precision * representation of output int32_t. * * Generally 0 < volumePrecision <= 14 (due to the limits of * int16_t values for Volume). volumePrecision cannot be 0 due * to rounding and shifts. */ const int volumePrecision = 12; // in bits resampler->setSampleRate(input_freq); resampler->setVolume(0x1000, 0x1000); resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); if (profiling) { const int looplimit = 100; if (profileResample) { /* * For profiling on mobile devices, upon experimentation * it is better to run a few trials with a shorter loop limit, * and take the minimum time. * * Long tests can cause CPU temperature to build up and thermal throttling * to reduce CPU frequency. * * For frequency checks (index=0, or 1, etc.): * "cat /sys/devices/system/cpu/cpu${index}/cpufreq/scaling_*_freq" * * For temperature checks (index=0, or 1, etc.): * "cat /sys/class/thermal/thermal_zone${index}/temp" * * Another way to avoid thermal throttling is to fix the CPU frequency * at a lower level which prevents excessive temperatures. */ const int trials = 4; const int looplimit = 4; timespec start, end; int64_t time; for (int n = 0; n < trials; ++n) { clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < looplimit; ++i) { resampler->resample((int*) output_vaddr, out_frames, &provider); provider.reset(); // reset only provider as benchmarking provider.reset(); // during benchmarking reset only the provider } clock_gettime(CLOCK_MONOTONIC, &end); int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; int64_t time = end_ns - start_ns; printf("time(ns):%lld channels:%d quality:%d\n", time, channels, quality); printf("%f Mspl/s\n", out_frames * looplimit / (time / 1e9) / 1e6); int64_t diff_ns = end_ns - start_ns; if (n == 0 || diff_ns < time) { time = diff_ns; // save the best out of our trials. } } // Mfrms/s is "Millions of output frames per second". printf("quality: %d channels: %d msec: %lld Mfrms/s: %.2lf\n", quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6); resampler->reset(); } Loading @@ -266,19 +351,31 @@ int main(int argc, char* argv[]) { if (gVerbose) { printf("reset() complete\n"); } delete resampler; resampler = NULL; // mono takes left channel only // stereo right channel is half amplitude of stereo left channel (due to input creation) int32_t* out = (int32_t*) output_vaddr; int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t)); // round to half towards zero and saturate at int16 (non-dithered) const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0 for (size_t i = 0; i < out_frames; i++) { for (int j = 0; j < channels; j++) { int32_t s = out[i * 2 + j] >> 12; if (s > 32767) s = 32767; else if (s < -32768) int32_t s = out[i * 2 + j] + roundVal; // add offset here if (s < 0) { s = (s + 1) >> volumePrecision; // round to 0 if (s < -32768) { s = -32768; } } else { s = s >> volumePrecision; if (s > 32767) { s = 32767; } } convert[i * channels + j] = int16_t(s); } } Loading Loading
services/audioflinger/AudioResamplerDyn.cpp +38 −17 Original line number Diff line number Diff line Loading @@ -161,7 +161,8 @@ void AudioResamplerDyn::Constants::set( AudioResamplerDyn::AudioResamplerDyn(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality) : AudioResampler(bitDepth, inChannelCount, sampleRate, quality), mResampleType(0), mFilterSampleRate(0), mCoefBuffer(NULL) mResampleType(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY), mCoefBuffer(NULL) { mVolumeSimd[0] = mVolumeSimd[1] = 0; mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better Loading Loading @@ -213,19 +214,16 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, double stopBandAtten, // test the filter and report results double fp = (fcr - tbw/2)/c.mL; double fs = (fcr + tbw/2)/c.mL; double fmin, fmax; testFir(buf, c.mL, c.mHalfNumCoefs, 0., fp, 100, fmin, fmax); double d1 = (fmax - fmin)/2.; double ap = -20.*log10(1. - d1); // passband ripple printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, (fmax + fmin)/2., d1, ap); testFir(buf, c.mL, c.mHalfNumCoefs, fs, 0.5, 100, fmin, fmax); double d2 = fmax; double as = -20.*log10(d2); // stopband attenuation printf("stopband(%lf, %lf): %.8lf %.8lf %.3lf\n", fs, 0.5, (fmax + fmin)/2., d2, as); double passMin, passMax, passRipple; double stopMax, stopRipple; testFir(buf, c.mL, c.mHalfNumCoefs, fp, fs, /*passSteps*/ 1000, /*stopSteps*/ 100000, passMin, passMax, passRipple, stopMax, stopRipple); printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, passMin, passMax, passRipple); printf("stopband(%lf, %lf): %.8lf %.3lf\n", fs, 0.5, stopMax, stopRipple); #endif } // recursive gcd (TODO: verify tail recursion elimination should make this iterate) // recursive gcd. Using objdump, it appears the tail recursion is converted to a while loop. static int gcd(int n, int m) { if (m == 0) { return n; Loading @@ -233,7 +231,16 @@ static int gcd(int n, int m) { return gcd(m, n % m); } static bool isClose(int32_t newSampleRate, int32_t prevSampleRate, int32_t filterSampleRate) { static bool isClose(int32_t newSampleRate, int32_t prevSampleRate, int32_t filterSampleRate, int32_t outSampleRate) { // different upsampling ratios do not need a filter change. if (filterSampleRate != 0 && filterSampleRate < outSampleRate && newSampleRate < outSampleRate) return true; // check design criteria again if downsampling is detected. int pdiff = absdiff(newSampleRate, prevSampleRate); int adiff = absdiff(newSampleRate, filterSampleRate); Loading @@ -255,8 +262,10 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { // TODO: Add precalculated Equiripple filters if (!isClose(inSampleRate, oldSampleRate, mFilterSampleRate)) { if (mFilterQuality != getQuality() || !isClose(inSampleRate, oldSampleRate, mFilterSampleRate, mSampleRate)) { mFilterSampleRate = inSampleRate; mFilterQuality = getQuality(); // Begin Kaiser Filter computation // Loading @@ -270,12 +279,12 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { double stopBandAtten; double tbwCheat = 1.; // how much we "cheat" into aliasing int halfLength; if (getQuality() == DYN_HIGH_QUALITY) { if (mFilterQuality == DYN_HIGH_QUALITY) { // 32b coefficients, 64 length useS32 = true; stopBandAtten = 98.; halfLength = 32; } else if (getQuality() == DYN_LOW_QUALITY) { } else if (mFilterQuality == DYN_LOW_QUALITY) { // 16b coefficients, 16-32 length useS32 = false; stopBandAtten = 80.; Loading @@ -289,8 +298,9 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { } else { tbwCheat = 1.03; } } else { // medium quality } else { // DYN_MED_QUALITY // 16b coefficients, 32-64 length // note: > 64 length filters with 16b coefs can have quantization noise problems useS32 = false; stopBandAtten = 84.; if (mSampleRate >= inSampleRate * 4) { Loading @@ -315,9 +325,20 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) { int phases = mSampleRate / gcd(mSampleRate, inSampleRate); while (phases<63) { // too few phases, allow room for interpolation // TODO: Once dynamic sample rate change is an option, the code below // should be modified to execute only when dynamic sample rate change is enabled. // // as above, #phases less than 63 is too few phases for accurate linear interpolation. // we increase the phases to compensate, but more phases means more memory per // filter and more time to compute the filter. // // if we know that the filter will be used for dynamic sample rate changes, // that would allow us skip this part for fixed sample rate resamplers. // while (phases<63) { phases *= 2; // this code only needed to support dynamic rate changes } if (phases>=256) { // too many phases, always interpolate phases = 127; } Loading
services/audioflinger/AudioResamplerDyn.h +2 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,8 @@ private: Constants mConstants; // current set of coefficient parameters int32_t __attribute__ ((aligned (8))) mVolumeSimd[2]; int32_t mResampleType; // contains the resample type. int32_t mFilterSampleRate; // designed sample rate for the filter int32_t mFilterSampleRate; // designed filter sample rate. src_quality mFilterQuality; // designed filter quality. void* mCoefBuffer; // if a filter is created, this is not null }; Loading
services/audioflinger/AudioResamplerFirGen.h +428 −51 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/audioflinger/test-resample.cpp +118 −21 Original line number Diff line number Diff line Loading @@ -57,7 +57,8 @@ static int usage(const char* name) { int main(int argc, char* argv[]) { const char* const progname = argv[0]; bool profiling = false; bool profileResample = false; bool profileFilter = false; bool writeHeader = false; int channels = 1; int input_freq = 0; Loading @@ -65,10 +66,13 @@ int main(int argc, char* argv[]) { AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY; int ch; while ((ch = getopt(argc, argv, "phvsq:i:o:")) != -1) { while ((ch = getopt(argc, argv, "pfhvsq:i:o:")) != -1) { switch (ch) { case 'p': profiling = true; profileResample = true; break; case 'f': profileFilter = true; break; case 'h': writeHeader = true; Loading Loading @@ -230,27 +234,108 @@ int main(int argc, char* argv[]) { size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq; output_size &= ~7; // always stereo, 32-bits if (profileFilter) { // Check how fast sample rate changes are that require filter changes. // The delta sample rate changes must indicate a downsampling ratio, // and must be larger than 10% changes. // // On fast devices, filters should be generated between 0.1ms - 1ms. // (single threaded). AudioResampler* resampler = AudioResampler::create(16, channels, 8000, quality); int looplimit = 100; timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < looplimit; ++i) { resampler->setSampleRate(9000); resampler->setSampleRate(12000); resampler->setSampleRate(20000); resampler->setSampleRate(30000); } clock_gettime(CLOCK_MONOTONIC, &end); int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; int64_t time = end_ns - start_ns; printf("%.2f sample rate changes with filter calculation/sec\n", looplimit * 4 / (time / 1e9)); // Check how fast sample rate changes are without filter changes. // This should be very fast, probably 0.1us - 1us per sample rate // change. resampler->setSampleRate(1000); looplimit = 1000; clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < looplimit; ++i) { resampler->setSampleRate(1000+i); } clock_gettime(CLOCK_MONOTONIC, &end); start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; time = end_ns - start_ns; printf("%.2f sample rate changes without filter calculation/sec\n", looplimit / (time / 1e9)); resampler->reset(); delete resampler; } void* output_vaddr = malloc(output_size); AudioResampler* resampler = AudioResampler::create(16, channels, output_freq, quality); size_t out_frames = output_size/8; /* set volume precision to 12 bits, so the volume scale is 1<<12. * This means the "integer" part fits in the Q19.12 precision * representation of output int32_t. * * Generally 0 < volumePrecision <= 14 (due to the limits of * int16_t values for Volume). volumePrecision cannot be 0 due * to rounding and shifts. */ const int volumePrecision = 12; // in bits resampler->setSampleRate(input_freq); resampler->setVolume(0x1000, 0x1000); resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); if (profiling) { const int looplimit = 100; if (profileResample) { /* * For profiling on mobile devices, upon experimentation * it is better to run a few trials with a shorter loop limit, * and take the minimum time. * * Long tests can cause CPU temperature to build up and thermal throttling * to reduce CPU frequency. * * For frequency checks (index=0, or 1, etc.): * "cat /sys/devices/system/cpu/cpu${index}/cpufreq/scaling_*_freq" * * For temperature checks (index=0, or 1, etc.): * "cat /sys/class/thermal/thermal_zone${index}/temp" * * Another way to avoid thermal throttling is to fix the CPU frequency * at a lower level which prevents excessive temperatures. */ const int trials = 4; const int looplimit = 4; timespec start, end; int64_t time; for (int n = 0; n < trials; ++n) { clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < looplimit; ++i) { resampler->resample((int*) output_vaddr, out_frames, &provider); provider.reset(); // reset only provider as benchmarking provider.reset(); // during benchmarking reset only the provider } clock_gettime(CLOCK_MONOTONIC, &end); int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; int64_t time = end_ns - start_ns; printf("time(ns):%lld channels:%d quality:%d\n", time, channels, quality); printf("%f Mspl/s\n", out_frames * looplimit / (time / 1e9) / 1e6); int64_t diff_ns = end_ns - start_ns; if (n == 0 || diff_ns < time) { time = diff_ns; // save the best out of our trials. } } // Mfrms/s is "Millions of output frames per second". printf("quality: %d channels: %d msec: %lld Mfrms/s: %.2lf\n", quality, channels, time/1000000, out_frames * looplimit / (time / 1e9) / 1e6); resampler->reset(); } Loading @@ -266,19 +351,31 @@ int main(int argc, char* argv[]) { if (gVerbose) { printf("reset() complete\n"); } delete resampler; resampler = NULL; // mono takes left channel only // stereo right channel is half amplitude of stereo left channel (due to input creation) int32_t* out = (int32_t*) output_vaddr; int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t)); // round to half towards zero and saturate at int16 (non-dithered) const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0 for (size_t i = 0; i < out_frames; i++) { for (int j = 0; j < channels; j++) { int32_t s = out[i * 2 + j] >> 12; if (s > 32767) s = 32767; else if (s < -32768) int32_t s = out[i * 2 + j] + roundVal; // add offset here if (s < 0) { s = (s + 1) >> volumePrecision; // round to 0 if (s < -32768) { s = -32768; } } else { s = s >> volumePrecision; if (s > 32767) { s = 32767; } } convert[i * channels + j] = int16_t(s); } } Loading