diff options
18 files changed, 1039 insertions, 423 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 5d98d98..33378db 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -602,11 +602,11 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) { // output queue, and handles it in renderer instead. rememberCodecSpecificData(newFormat); onFlush(false /* notifyComplete */); - err = OK; + continue; } else if (seamlessFormatChange) { // reuse existing decoder and don't flush rememberCodecSpecificData(newFormat); - err = OK; + continue; } else { // This stream is unaffected by the discontinuity return -EWOULDBLOCK; @@ -696,10 +696,7 @@ bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) { int32_t streamErr = ERROR_END_OF_STREAM; CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); - if (streamErr == OK) { - /* buffers are returned to hold on to */ - return true; - } + CHECK(streamErr != OK); // attempt to queue EOS status_t err = mCodec->queueInputBuffer( diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index be8cf46..07ea605 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,10 +1,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifeq ($(TARGET_DEVICE), manta) - LOCAL_CFLAGS += -DSURFACE_IS_BGR32 -endif - LOCAL_SRC_FILES:= \ FrameDropper.cpp \ GraphicBufferSource.cpp \ diff --git a/services/audioflinger/AudioResamplerFirOps.h b/services/audioflinger/AudioResamplerFirOps.h index 563c22f..658285d 100644 --- a/services/audioflinger/AudioResamplerFirOps.h +++ b/services/audioflinger/AudioResamplerFirOps.h @@ -25,7 +25,7 @@ namespace android { #define USE_INLINE_ASSEMBLY (false) #endif -#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__) +#if defined(__aarch64__) || defined(__ARM_NEON__) #define USE_NEON (true) #include <arm_neon.h> #else diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h index 8a615a7..176202e 100644 --- a/services/audioflinger/AudioResamplerFirProcess.h +++ b/services/audioflinger/AudioResamplerFirProcess.h @@ -243,6 +243,9 @@ void ProcessBase(TO* const out, } } +/* Calculates a single output frame from a polyphase resampling filter. + * See Process() for parameter details. + */ template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO> static inline void ProcessL(TO* const out, @@ -256,6 +259,39 @@ void ProcessL(TO* const out, ProcessBase<CHANNELS, STRIDE, InterpNull>(out, count, coefsP, coefsN, sP, sN, 0, volumeLR); } +/* + * Calculates a single output frame from a polyphase resampling filter, + * with filter phase interpolation. + * + * @param out should point to the output buffer with space for at least one output frame. + * + * @param count should be half the size of the total filter length (halfNumCoefs), as we + * use symmetry in filter coefficients to evaluate two dot products. + * + * @param coefsP is one phase of the polyphase filter bank of size halfNumCoefs, corresponding + * to the positive sP. + * + * @param coefsN is one phase of the polyphase filter bank of size halfNumCoefs, corresponding + * to the negative sN. + * + * @param coefsP1 is the next phase of coefsP (used for interpolation). + * + * @param coefsN1 is the next phase of coefsN (used for interpolation). + * + * @param sP is the positive half of the coefficients (as viewed by a convolution), + * starting at the original samples pointer and decrementing (by CHANNELS). + * + * @param sN is the negative half of the samples (as viewed by a convolution), + * starting at the original samples pointer + CHANNELS and incrementing (by CHANNELS). + * + * @param lerpP The fractional siting between the polyphase indices is given by the bits + * below coefShift. See fir() for details. + * + * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel, + * expressed as a S32 integer or float. A negative value inverts the channel 180 degrees. + * The pointer volumeLR should be aligned to a minimum of 8 bytes. + * A typical value for volume is 0x1000 to align to a unity gain output of 20.12. + */ template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO, typename TINTERP> static inline void Process(TO* const out, @@ -274,7 +310,7 @@ void Process(TO* const out, } /* - * Calculates a single output frame (two samples) from input sample pointer. + * Calculates a single output frame from input sample pointer. * * This sets up the params for the accelerated Process() and ProcessL() * functions to do the appropriate dot products. @@ -309,7 +345,7 @@ void Process(TO* const out, * the positive half of the filter is dot product from samples to samples-halfNumCoefs+1. * * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel, - * expressed as a S32 integer. A negative value inverts the channel 180 degrees. + * expressed as a S32 integer or float. A negative value inverts the channel 180 degrees. * The pointer volumeLR should be aligned to a minimum of 8 bytes. * A typical value for volume is 0x1000 to align to a unity gain output of 20.12. * diff --git a/services/audioflinger/AudioResamplerFirProcessNeon.h b/services/audioflinger/AudioResamplerFirProcessNeon.h index 107175b..3de9edd 100644 --- a/services/audioflinger/AudioResamplerFirProcessNeon.h +++ b/services/audioflinger/AudioResamplerFirProcessNeon.h @@ -22,10 +22,35 @@ namespace android { // depends on AudioResamplerFirOps.h, AudioResamplerFirProcess.h #if USE_NEON + +// use intrinsics if inline arm32 assembly is not possible +#if !USE_INLINE_ASSEMBLY +#define USE_INTRINSIC +#endif + +// following intrinsics available only on ARM 64 bit ACLE +#ifndef __aarch64__ +#undef vld1q_f32_x2 +#undef vld1q_s32_x2 +#endif + +#define TO_STRING2(x) #x +#define TO_STRING(x) TO_STRING2(x) +// uncomment to print GCC version, may be relevant for intrinsic optimizations +/* #pragma message ("GCC version: " TO_STRING(__GNUC__) \ + "." TO_STRING(__GNUC_MINOR__) \ + "." TO_STRING(__GNUC_PATCHLEVEL__)) */ + +// +// NEON specializations are enabled for Process() and ProcessL() in AudioResamplerFirProcess.h +// +// Two variants are presented here: +// ARM NEON inline assembly which appears up to 10-15% faster than intrinsics (gcc 4.9) for arm32. +// ARM NEON intrinsics which can also be used by arm64 and x86/64 with NEON header. // -// NEON specializations are enabled for Process() and ProcessL() // Macros to save a mono/stereo accumulator sample in q0 (and q4) as stereo out. +// These are only used for inline assembly. #define ASSEMBLY_ACCUMULATE_MONO \ "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes */\ "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output */\ @@ -45,6 +70,458 @@ namespace android { "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating)*/\ "vst1.s32 {d3}, %[out] \n"/* (2+2d)store result*/ +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(int32_t* out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* volumeLR, + uint32_t lerpP, + const int16_t* coefsP1, + const int16_t* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const int16_t*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const int16_t*)__builtin_assume_aligned(coefsN, 16); + + int16x4_t interp; + if (!FIXED) { + interp = vdup_n_s16(lerpP); + //interp = (int16x4_t)vset_lane_s32 ((int32x2_t)lerpP, interp, 0); + coefsP1 = (const int16_t*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const int16_t*)__builtin_assume_aligned(coefsN1, 16); + } + int32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_s32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_s32(0); + } + do { + int16x8_t posCoef = vld1q_s16(coefsP); + coefsP += 8; + int16x8_t negCoef = vld1q_s16(coefsN); + coefsN += 8; + if (!FIXED) { // interpolate + int16x8_t posCoef1 = vld1q_s16(coefsP1); + coefsP1 += 8; + int16x8_t negCoef1 = vld1q_s16(coefsN1); + coefsN1 += 8; + + posCoef1 = vsubq_s16(posCoef1, posCoef); + negCoef = vsubq_s16(negCoef, negCoef1); + + posCoef1 = vqrdmulhq_lane_s16(posCoef1, interp, 0); + negCoef = vqrdmulhq_lane_s16(negCoef, interp, 0); + + posCoef = vaddq_s16(posCoef, posCoef1); + negCoef = vaddq_s16(negCoef, negCoef1); + } + switch (CHANNELS) { + case 1: { + int16x8_t posSamp = vld1q_s16(sP); + int16x8_t negSamp = vld1q_s16(sN); + sN += 8; + posSamp = vrev64q_s16(posSamp); + + // dot product + accum = vmlal_s16(accum, vget_low_s16(posSamp), vget_high_s16(posCoef)); // reversed + accum = vmlal_s16(accum, vget_high_s16(posSamp), vget_low_s16(posCoef)); // reversed + accum = vmlal_s16(accum, vget_low_s16(negSamp), vget_low_s16(negCoef)); + accum = vmlal_s16(accum, vget_high_s16(negSamp), vget_high_s16(negCoef)); + sP -= 8; + } break; + case 2: { + int16x8x2_t posSamp = vld2q_s16(sP); + int16x8x2_t negSamp = vld2q_s16(sN); + sN += 16; + posSamp.val[0] = vrev64q_s16(posSamp.val[0]); + posSamp.val[1] = vrev64q_s16(posSamp.val[1]); + + // dot product + accum = vmlal_s16(accum, vget_low_s16(posSamp.val[0]), vget_high_s16(posCoef)); // r + accum = vmlal_s16(accum, vget_high_s16(posSamp.val[0]), vget_low_s16(posCoef)); // r + accum2 = vmlal_s16(accum2, vget_low_s16(posSamp.val[1]), vget_high_s16(posCoef)); // r + accum2 = vmlal_s16(accum2, vget_high_s16(posSamp.val[1]), vget_low_s16(posCoef)); // r + accum = vmlal_s16(accum, vget_low_s16(negSamp.val[0]), vget_low_s16(negCoef)); + accum = vmlal_s16(accum, vget_high_s16(negSamp.val[0]), vget_high_s16(negCoef)); + accum2 = vmlal_s16(accum2, vget_low_s16(negSamp.val[1]), vget_low_s16(negCoef)); + accum2 = vmlal_s16(accum2, vget_high_s16(negSamp.val[1]), vget_high_s16(negCoef)); + sP -= 16; + } + } break; + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const int32_t*)__builtin_assume_aligned(volumeLR, 8); + int32x2_t vLR = vld1_s32(volumeLR); + int32x2_t outSamp = vld1_s32(out); + // combine and funnel down accumulator + int32x2_t outAccum = vpadd_s32(vget_low_s32(accum), vget_high_s32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_s32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + int32x2_t outAccum2 = vpadd_s32(vget_low_s32(accum2), vget_high_s32(accum2)); + outAccum = vpadd_s32(outAccum, outAccum2); + } + outAccum = vqrdmulh_s32(outAccum, vLR); + outSamp = vqadd_s32(outSamp, outAccum); + vst1_s32(out, outSamp); +} + +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(int32_t* out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* volumeLR, + uint32_t lerpP, + const int32_t* coefsP1, + const int32_t* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16); + + int32x2_t interp; + if (!FIXED) { + interp = vdup_n_s32(lerpP); + coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16); + } + int32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_s32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_s32(0); + } + do { +#ifdef vld1q_s32_x2 + int32x4x2_t posCoef = vld1q_s32_x2(coefsP); + coefsP += 8; + int32x4x2_t negCoef = vld1q_s32_x2(coefsN); + coefsN += 8; +#else + int32x4x2_t posCoef; + posCoef.val[0] = vld1q_s32(coefsP); + coefsP += 4; + posCoef.val[1] = vld1q_s32(coefsP); + coefsP += 4; + int32x4x2_t negCoef; + negCoef.val[0] = vld1q_s32(coefsN); + coefsN += 4; + negCoef.val[1] = vld1q_s32(coefsN); + coefsN += 4; +#endif + if (!FIXED) { // interpolate +#ifdef vld1q_s32_x2 + int32x4x2_t posCoef1 = vld1q_s32_x2(coefsP1); + coefsP1 += 8; + int32x4x2_t negCoef1 = vld1q_s32_x2(coefsN1); + coefsN1 += 8; +#else + int32x4x2_t posCoef1; + posCoef1.val[0] = vld1q_s32(coefsP1); + coefsP1 += 4; + posCoef1.val[1] = vld1q_s32(coefsP1); + coefsP1 += 4; + int32x4x2_t negCoef1; + negCoef1.val[0] = vld1q_s32(coefsN1); + coefsN1 += 4; + negCoef1.val[1] = vld1q_s32(coefsN1); + coefsN1 += 4; +#endif + + posCoef1.val[0] = vsubq_s32(posCoef1.val[0], posCoef.val[0]); + posCoef1.val[1] = vsubq_s32(posCoef1.val[1], posCoef.val[1]); + negCoef.val[0] = vsubq_s32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vsubq_s32(negCoef.val[1], negCoef1.val[1]); + + posCoef1.val[0] = vqrdmulhq_lane_s32(posCoef1.val[0], interp, 0); + posCoef1.val[1] = vqrdmulhq_lane_s32(posCoef1.val[1], interp, 0); + negCoef.val[0] = vqrdmulhq_lane_s32(negCoef.val[0], interp, 0); + negCoef.val[1] = vqrdmulhq_lane_s32(negCoef.val[1], interp, 0); + + posCoef.val[0] = vaddq_s32(posCoef.val[0], posCoef1.val[0]); + posCoef.val[1] = vaddq_s32(posCoef.val[1], posCoef1.val[1]); + negCoef.val[0] = vaddq_s32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vaddq_s32(negCoef.val[1], negCoef1.val[1]); + } + switch (CHANNELS) { + case 1: { + int16x8_t posSamp = vld1q_s16(sP); + int16x8_t negSamp = vld1q_s16(sN); + sN += 8; + posSamp = vrev64q_s16(posSamp); + + int32x4_t posSamp0 = vshll_n_s16(vget_low_s16(posSamp), 15); + int32x4_t posSamp1 = vshll_n_s16(vget_high_s16(posSamp), 15); + int32x4_t negSamp0 = vshll_n_s16(vget_low_s16(negSamp), 15); + int32x4_t negSamp1 = vshll_n_s16(vget_high_s16(negSamp), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum = vaddq_s32(accum, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum = vaddq_s32(accum, posSamp1); + accum = vaddq_s32(accum, negSamp0); + + sP -= 8; + } break; + case 2: { + int16x8x2_t posSamp = vld2q_s16(sP); + int16x8x2_t negSamp = vld2q_s16(sN); + sN += 16; + posSamp.val[0] = vrev64q_s16(posSamp.val[0]); + posSamp.val[1] = vrev64q_s16(posSamp.val[1]); + + // left + int32x4_t posSamp0 = vshll_n_s16(vget_low_s16(posSamp.val[0]), 15); + int32x4_t posSamp1 = vshll_n_s16(vget_high_s16(posSamp.val[0]), 15); + int32x4_t negSamp0 = vshll_n_s16(vget_low_s16(negSamp.val[0]), 15); + int32x4_t negSamp1 = vshll_n_s16(vget_high_s16(negSamp.val[0]), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum = vaddq_s32(accum, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum = vaddq_s32(accum, posSamp1); + accum = vaddq_s32(accum, negSamp0); + + // right + posSamp0 = vshll_n_s16(vget_low_s16(posSamp.val[1]), 15); + posSamp1 = vshll_n_s16(vget_high_s16(posSamp.val[1]), 15); + negSamp0 = vshll_n_s16(vget_low_s16(negSamp.val[1]), 15); + negSamp1 = vshll_n_s16(vget_high_s16(negSamp.val[1]), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum2 = vaddq_s32(accum2, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum2 = vaddq_s32(accum2, posSamp1); + accum2 = vaddq_s32(accum2, negSamp0); + + sP -= 16; + } break; + } + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const int32_t*)__builtin_assume_aligned(volumeLR, 8); + int32x2_t vLR = vld1_s32(volumeLR); + int32x2_t outSamp = vld1_s32(out); + // combine and funnel down accumulator + int32x2_t outAccum = vpadd_s32(vget_low_s32(accum), vget_high_s32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_s32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + int32x2_t outAccum2 = vpadd_s32(vget_low_s32(accum2), vget_high_s32(accum2)); + outAccum = vpadd_s32(outAccum, outAccum2); + } + outAccum = vqrdmulh_s32(outAccum, vLR); + outSamp = vqadd_s32(outSamp, outAccum); + vst1_s32(out, outSamp); +} + +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(float* out, + int count, + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* volumeLR, + float lerpP, + const float* coefsP1, + const float* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const float*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const float*)__builtin_assume_aligned(coefsN, 16); + + float32x2_t interp; + if (!FIXED) { + interp = vdup_n_f32(lerpP); + coefsP1 = (const float*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const float*)__builtin_assume_aligned(coefsN1, 16); + } + float32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_f32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_f32(0); + } + do { +#ifdef vld1q_f32_x2 + float32x4x2_t posCoef = vld1q_f32_x2(coefsP); + coefsP += 8; + float32x4x2_t negCoef = vld1q_f32_x2(coefsN); + coefsN += 8; +#else + float32x4x2_t posCoef; + posCoef.val[0] = vld1q_f32(coefsP); + coefsP += 4; + posCoef.val[1] = vld1q_f32(coefsP); + coefsP += 4; + float32x4x2_t negCoef; + negCoef.val[0] = vld1q_f32(coefsN); + coefsN += 4; + negCoef.val[1] = vld1q_f32(coefsN); + coefsN += 4; +#endif + if (!FIXED) { // interpolate +#ifdef vld1q_f32_x2 + float32x4x2_t posCoef1 = vld1q_f32_x2(coefsP1); + coefsP1 += 8; + float32x4x2_t negCoef1 = vld1q_f32_x2(coefsN1); + coefsN1 += 8; +#else + float32x4x2_t posCoef1; + posCoef1.val[0] = vld1q_f32(coefsP1); + coefsP1 += 4; + posCoef1.val[1] = vld1q_f32(coefsP1); + coefsP1 += 4; + float32x4x2_t negCoef1; + negCoef1.val[0] = vld1q_f32(coefsN1); + coefsN1 += 4; + negCoef1.val[1] = vld1q_f32(coefsN1); + coefsN1 += 4; +#endif + posCoef1.val[0] = vsubq_f32(posCoef1.val[0], posCoef.val[0]); + posCoef1.val[1] = vsubq_f32(posCoef1.val[1], posCoef.val[1]); + negCoef.val[0] = vsubq_f32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vsubq_f32(negCoef.val[1], negCoef1.val[1]); + + posCoef.val[0] = vmlaq_lane_f32(posCoef.val[0], posCoef1.val[0], interp, 0); + posCoef.val[1] = vmlaq_lane_f32(posCoef.val[1], posCoef1.val[1], interp, 0); + negCoef.val[0] = vmlaq_lane_f32(negCoef1.val[0], negCoef.val[0], interp, 0); // rev + negCoef.val[1] = vmlaq_lane_f32(negCoef1.val[1], negCoef.val[1], interp, 0); // rev + } + switch (CHANNELS) { + case 1: { +#ifdef vld1q_f32_x2 + float32x4x2_t posSamp = vld1q_f32_x2(sP); + float32x4x2_t negSamp = vld1q_f32_x2(sN); + sN += 8; + sP -= 8; +#else + float32x4x2_t posSamp; + posSamp.val[0] = vld1q_f32(sP); + sP += 4; + posSamp.val[1] = vld1q_f32(sP); + sP -= 12; + float32x4x2_t negSamp; + negSamp.val[0] = vld1q_f32(sN); + sN += 4; + negSamp.val[1] = vld1q_f32(sN); + sN += 4; +#endif + // effectively we want a vrev128q_f32() + posSamp.val[0] = vrev64q_f32(posSamp.val[0]); + posSamp.val[1] = vrev64q_f32(posSamp.val[1]); + posSamp.val[0] = vcombine_f32( + vget_high_f32(posSamp.val[0]), vget_low_f32(posSamp.val[0])); + posSamp.val[1] = vcombine_f32( + vget_high_f32(posSamp.val[1]), vget_low_f32(posSamp.val[1])); + + accum = vmlaq_f32(accum, posSamp.val[0], posCoef.val[1]); + accum = vmlaq_f32(accum, posSamp.val[1], posCoef.val[0]); + accum = vmlaq_f32(accum, negSamp.val[0], negCoef.val[0]); + accum = vmlaq_f32(accum, negSamp.val[1], negCoef.val[1]); + } break; + case 2: { + float32x4x2_t posSamp0 = vld2q_f32(sP); + sP += 8; + float32x4x2_t negSamp0 = vld2q_f32(sN); + sN += 8; + posSamp0.val[0] = vrev64q_f32(posSamp0.val[0]); + posSamp0.val[1] = vrev64q_f32(posSamp0.val[1]); + posSamp0.val[0] = vcombine_f32( + vget_high_f32(posSamp0.val[0]), vget_low_f32(posSamp0.val[0])); + posSamp0.val[1] = vcombine_f32( + vget_high_f32(posSamp0.val[1]), vget_low_f32(posSamp0.val[1])); + + float32x4x2_t posSamp1 = vld2q_f32(sP); + sP -= 24; + float32x4x2_t negSamp1 = vld2q_f32(sN); + sN += 8; + posSamp1.val[0] = vrev64q_f32(posSamp1.val[0]); + posSamp1.val[1] = vrev64q_f32(posSamp1.val[1]); + posSamp1.val[0] = vcombine_f32( + vget_high_f32(posSamp1.val[0]), vget_low_f32(posSamp1.val[0])); + posSamp1.val[1] = vcombine_f32( + vget_high_f32(posSamp1.val[1]), vget_low_f32(posSamp1.val[1])); + + // Note: speed is affected by accumulation order. + // Also, speed appears slower using vmul/vadd instead of vmla for + // stereo case, comparable for mono. + + accum = vmlaq_f32(accum, negSamp0.val[0], negCoef.val[0]); + accum = vmlaq_f32(accum, negSamp1.val[0], negCoef.val[1]); + accum2 = vmlaq_f32(accum2, negSamp0.val[1], negCoef.val[0]); + accum2 = vmlaq_f32(accum2, negSamp1.val[1], negCoef.val[1]); + + accum = vmlaq_f32(accum, posSamp0.val[0], posCoef.val[1]); // reversed + accum = vmlaq_f32(accum, posSamp1.val[0], posCoef.val[0]); // reversed + accum2 = vmlaq_f32(accum2, posSamp0.val[1], posCoef.val[1]); // reversed + accum2 = vmlaq_f32(accum2, posSamp1.val[1], posCoef.val[0]); // reversed + } break; + } + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const float*)__builtin_assume_aligned(volumeLR, 8); + float32x2_t vLR = vld1_f32(volumeLR); + float32x2_t outSamp = vld1_f32(out); + // combine and funnel down accumulator + float32x2_t outAccum = vpadd_f32(vget_low_f32(accum), vget_high_f32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_f32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + float32x2_t outAccum2 = vpadd_f32(vget_low_f32(accum2), vget_high_f32(accum2)); + outAccum = vpadd_f32(outAccum, outAccum2); + } + outSamp = vmla_f32(outSamp, outAccum, vLR); + vst1_f32(out, outSamp); +} + template <> inline void ProcessL<1, 16>(int32_t* const out, int count, @@ -54,6 +531,10 @@ inline void ProcessL<1, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -95,6 +576,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "q0", "q1", "q2", "q3", "q8", "q10" ); +#endif } template <> @@ -106,6 +588,10 @@ inline void ProcessL<2, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -153,6 +639,7 @@ inline void ProcessL<2, 16>(int32_t* const out, "q4", "q5", "q6", "q8", "q10" ); +#endif } template <> @@ -167,6 +654,11 @@ inline void Process<1, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else + const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -223,6 +715,7 @@ inline void Process<1, 16>(int32_t* const out, "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11" ); +#endif } template <> @@ -237,6 +730,10 @@ inline void Process<2, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -299,6 +796,7 @@ inline void Process<2, 16>(int32_t* const out, "q4", "q5", "q6", "q8", "q9", "q10", "q11" ); +#endif } template <> @@ -310,6 +808,10 @@ inline void ProcessL<1, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -360,6 +862,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -371,6 +874,10 @@ inline void ProcessL<2, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -440,6 +947,7 @@ inline void ProcessL<2, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -454,6 +962,10 @@ inline void Process<1, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -525,6 +1037,7 @@ inline void Process<1, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -539,6 +1052,10 @@ inline void Process<2, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -629,6 +1146,65 @@ inline void Process<2, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif +} + +template<> +inline void ProcessL<1, 16>(float* const out, + int count, + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* const volumeLR) +{ + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +} + +template<> +inline void ProcessL<2, 16>(float* const out, + int count, + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* const volumeLR) +{ + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +} + +template<> +inline void Process<1, 16>(float* const out, + int count, + const float* coefsP, + const float* coefsN, + const float* coefsP1, + const float* coefsN1, + const float* sP, + const float* sN, + float lerpP, + const float* const volumeLR) +{ + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +} + +template<> +inline void Process<2, 16>(float* const out, + int count, + const float* coefsP, + const float* coefsN, + const float* coefsP1, + const float* coefsN1, + const float* sP, + const float* sN, + float lerpP, + const float* const volumeLR) +{ + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); } #endif //USE_NEON diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp index 255496e..9e7e8a4 100644 --- a/services/audioflinger/FastCapture.cpp +++ b/services/audioflinger/FastCapture.cpp @@ -29,18 +29,18 @@ namespace android { -/*static*/ const FastCaptureState FastCapture::initial; +/*static*/ const FastCaptureState FastCapture::sInitial; FastCapture::FastCapture() : FastThread(), - inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0), - readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0), - // dummyDumpState - totalNativeFramesRead(0) + mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), + mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0), + // mDummyDumpState + mTotalNativeFramesRead(0) { - previous = &initial; - current = &initial; + mPrevious = &sInitial; + mCurrent = &sInitial; - mDummyDumpState = &dummyDumpState; + mDummyDumpState = &mDummyFastCaptureDumpState; } FastCapture::~FastCapture() @@ -63,13 +63,13 @@ void FastCapture::setLog(NBLog::Writer *logWriter __unused) void FastCapture::onIdle() { - preIdle = *(const FastCaptureState *)current; - current = &preIdle; + mPreIdle = *(const FastCaptureState *)mCurrent; + mCurrent = &mPreIdle; } void FastCapture::onExit() { - delete[] readBuffer; + free(mReadBuffer); } bool FastCapture::isSubClassCommand(FastThreadState::Command command) @@ -86,69 +86,67 @@ bool FastCapture::isSubClassCommand(FastThreadState::Command command) void FastCapture::onStateChange() { - const FastCaptureState * const current = (const FastCaptureState *) this->current; - const FastCaptureState * const previous = (const FastCaptureState *) this->previous; - FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; + const FastCaptureState * const current = (const FastCaptureState *) mCurrent; + const FastCaptureState * const previous = (const FastCaptureState *) mPrevious; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; bool eitherChanged = false; // check for change in input HAL configuration - NBAIO_Format previousFormat = format; - if (current->mInputSourceGen != inputSourceGen) { - inputSource = current->mInputSource; - inputSourceGen = current->mInputSourceGen; - if (inputSource == NULL) { - format = Format_Invalid; - sampleRate = 0; + NBAIO_Format previousFormat = mFormat; + if (current->mInputSourceGen != mInputSourceGen) { + mInputSource = current->mInputSource; + mInputSourceGen = current->mInputSourceGen; + if (mInputSource == NULL) { + mFormat = Format_Invalid; + mSampleRate = 0; } else { - format = inputSource->format(); - sampleRate = Format_sampleRate(format); - unsigned channelCount = Format_channelCount(format); + mFormat = mInputSource->format(); + mSampleRate = Format_sampleRate(mFormat); + unsigned channelCount = Format_channelCount(mFormat); ALOG_ASSERT(channelCount == 1 || channelCount == 2); } - dumpState->mSampleRate = sampleRate; + dumpState->mSampleRate = mSampleRate; eitherChanged = true; } // check for change in pipe - if (current->mPipeSinkGen != pipeSinkGen) { - pipeSink = current->mPipeSink; - pipeSinkGen = current->mPipeSinkGen; + if (current->mPipeSinkGen != mPipeSinkGen) { + mPipeSink = current->mPipeSink; + mPipeSinkGen = current->mPipeSinkGen; eitherChanged = true; } // input source and pipe sink must be compatible - if (eitherChanged && inputSource != NULL && pipeSink != NULL) { - ALOG_ASSERT(Format_isEqual(format, pipeSink->format())); + if (eitherChanged && mInputSource != NULL && mPipeSink != NULL) { + ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format())); } - if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { - // FIXME to avoid priority inversion, don't delete here - delete[] readBuffer; - readBuffer = NULL; - if (frameCount > 0 && sampleRate > 0) { + if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { + // FIXME to avoid priority inversion, don't free here + free(mReadBuffer); + mReadBuffer = NULL; + if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal capture thread allocate for // us to avoid blocking here and to prevent possible priority inversion - unsigned channelCount = Format_channelCount(format); - // FIXME frameSize - readBuffer = new short[frameCount * channelCount]; - periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 - underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 - forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 - warmupNsMin = (frameCount * 750000000LL) / sampleRate; // 0.75 - warmupNsMax = (frameCount * 1250000000LL) / sampleRate; // 1.25 + (void)posix_memalign(&mReadBuffer, 32, frameCount * Format_frameSize(mFormat)); + mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 + mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 + mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 + mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 + mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 + mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { - periodNs = 0; - underrunNs = 0; - overrunNs = 0; - forceNs = 0; - warmupNsMin = 0; - warmupNsMax = LONG_MAX; + mPeriodNs = 0; + mUnderrunNs = 0; + mOverrunNs = 0; + mForceNs = 0; + mWarmupNsMin = 0; + mWarmupNsMax = LONG_MAX; } - readBufferState = -1; + mReadBufferState = -1; dumpState->mFrameCount = frameCount; } @@ -156,44 +154,43 @@ void FastCapture::onStateChange() void FastCapture::onWork() { - const FastCaptureState * const current = (const FastCaptureState *) this->current; - FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; - const FastCaptureState::Command command = this->command; + const FastCaptureState * const current = (const FastCaptureState *) mCurrent; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; + const FastCaptureState::Command command = mCommand; const size_t frameCount = current->mFrameCount; if ((command & FastCaptureState::READ) /*&& isWarm*/) { - ALOG_ASSERT(inputSource != NULL); - ALOG_ASSERT(readBuffer != NULL); + ALOG_ASSERT(mInputSource != NULL); + ALOG_ASSERT(mReadBuffer != NULL); dumpState->mReadSequence++; ATRACE_BEGIN("read"); - ssize_t framesRead = inputSource->read(readBuffer, frameCount, + ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount, AudioBufferProvider::kInvalidPTS); ATRACE_END(); dumpState->mReadSequence++; if (framesRead >= 0) { LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); - totalNativeFramesRead += framesRead; - dumpState->mFramesRead = totalNativeFramesRead; - readBufferState = framesRead; + mTotalNativeFramesRead += framesRead; + dumpState->mFramesRead = mTotalNativeFramesRead; + mReadBufferState = framesRead; } else { dumpState->mReadErrors++; - readBufferState = 0; + mReadBufferState = 0; } // FIXME rename to attemptedIO - attemptedWrite = true; + mAttemptedWrite = true; } if (command & FastCaptureState::WRITE) { - ALOG_ASSERT(pipeSink != NULL); - ALOG_ASSERT(readBuffer != NULL); - if (readBufferState < 0) { - unsigned channelCount = Format_channelCount(format); - // FIXME frameSize - memset(readBuffer, 0, frameCount * channelCount * sizeof(short)); - readBufferState = frameCount; + ALOG_ASSERT(mPipeSink != NULL); + ALOG_ASSERT(mReadBuffer != NULL); + if (mReadBufferState < 0) { + unsigned channelCount = Format_channelCount(mFormat); + memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat)); + mReadBufferState = frameCount; } - if (readBufferState > 0) { - ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState); + if (mReadBufferState > 0) { + ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState); // FIXME This supports at most one fast capture client. // To handle multiple clients this could be converted to an array, // or with a lot more work the control block could be shared by all clients. diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h index da0fe2f..e258a4d 100644 --- a/services/audioflinger/FastCapture.h +++ b/services/audioflinger/FastCapture.h @@ -46,19 +46,21 @@ private: virtual void onStateChange(); virtual void onWork(); - static const FastCaptureState initial; - FastCaptureState preIdle; // copy of state before we went into idle + static const FastCaptureState sInitial; + + FastCaptureState mPreIdle; // copy of state before we went into idle // FIXME by renaming, could pull up many of these to FastThread - NBAIO_Source *inputSource; - int inputSourceGen; - NBAIO_Sink *pipeSink; - int pipeSinkGen; - short *readBuffer; - ssize_t readBufferState; // number of initialized frames in readBuffer, or -1 to clear - NBAIO_Format format; - unsigned sampleRate; - FastCaptureDumpState dummyDumpState; - uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead + NBAIO_Source* mInputSource; + int mInputSourceGen; + NBAIO_Sink* mPipeSink; + int mPipeSinkGen; + void* mReadBuffer; + ssize_t mReadBufferState; // number of initialized frames in readBuffer, + // or -1 to clear + NBAIO_Format mFormat; + unsigned mSampleRate; + FastCaptureDumpState mDummyFastCaptureDumpState; + uint32_t mTotalNativeFramesRead; // copied to dumpState->mFramesRead }; // class FastCapture diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h index 17302d3..9bca2d4 100644 --- a/services/audioflinger/FastCaptureState.h +++ b/services/audioflinger/FastCaptureState.h @@ -29,20 +29,20 @@ struct FastCaptureState : FastThreadState { /*virtual*/ ~FastCaptureState(); // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread - NBAIO_Source *mInputSource; // HAL input device, must already be negotiated + NBAIO_Source* mInputSource; // HAL input device, must already be negotiated // FIXME by renaming, could pull up these fields to FastThreadState int mInputSourceGen; // increment when mInputSource is assigned - NBAIO_Sink *mPipeSink; // after reading from input source, write to this pipe sink + NBAIO_Sink* mPipeSink; // after reading from input source, write to this pipe sink int mPipeSinkGen; // increment when mPipeSink is assigned size_t mFrameCount; // number of frames per fast capture buffer - audio_track_cblk_t *mCblk; // control block for the single fast client, or NULL + audio_track_cblk_t* mCblk; // control block for the single fast client, or NULL // Extends FastThreadState::Command static const Command // The following commands also process configuration changes, and can be "or"ed: - READ = 0x8, // read from input source - WRITE = 0x10, // write to pipe sink - READ_WRITE = 0x18; // read from input source and write to pipe sink + READ = 0x8, // read from input source + WRITE = 0x10, // write to pipe sink + READ_WRITE = 0x18; // read from input source and write to pipe sink // never returns NULL; asserts if command is invalid static const char *commandToString(Command command); diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 220ebf3..e070f90 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -45,15 +45,15 @@ namespace android { -/*static*/ const FastMixerState FastMixer::initial; +/*static*/ const FastMixerState FastMixer::sInitial; FastMixer::FastMixer() : FastThread(), - slopNs(0), - // fastTrackNames - // generations - outputSink(NULL), - outputSinkGen(0), - mixer(NULL), + mSlopNs(0), + // mFastTrackNames + // mGenerations + mOutputSink(NULL), + mOutputSinkGen(0), + mMixer(NULL), mSinkBuffer(NULL), mSinkBufferSize(0), mSinkChannelCount(FCC_2), @@ -61,30 +61,30 @@ FastMixer::FastMixer() : FastThread(), mMixerBufferSize(0), mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), mMixerBufferState(UNDEFINED), - format(Format_Invalid), - sampleRate(0), - fastTracksGen(0), - totalNativeFramesWritten(0), + mFormat(Format_Invalid), + mSampleRate(0), + mFastTracksGen(0), + mTotalNativeFramesWritten(0), // timestamp - nativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler + mNativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler { - // FIXME pass initial as parameter to base class constructor, and make it static local - previous = &initial; - current = &initial; + // FIXME pass sInitial as parameter to base class constructor, and make it static local + mPrevious = &sInitial; + mCurrent = &sInitial; - mDummyDumpState = &dummyDumpState; + mDummyDumpState = &mDummyFastMixerDumpState; // TODO: Add channel mask to NBAIO_Format. // We assume that the channel mask must be a valid positional channel mask. mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); unsigned i; for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; - generations[i] = 0; + mFastTrackNames[i] = -1; + mGenerations[i] = 0; } #ifdef FAST_THREAD_STATISTICS - oldLoad.tv_sec = 0; - oldLoad.tv_nsec = 0; + mOldLoad.tv_sec = 0; + mOldLoad.tv_nsec = 0; #endif } @@ -104,20 +104,20 @@ const FastThreadState *FastMixer::poll() void FastMixer::setLog(NBLog::Writer *logWriter) { - if (mixer != NULL) { - mixer->setLog(logWriter); + if (mMixer != NULL) { + mMixer->setLog(logWriter); } } void FastMixer::onIdle() { - preIdle = *(const FastMixerState *)current; - current = &preIdle; + mPreIdle = *(const FastMixerState *)mCurrent; + mCurrent = &mPreIdle; } void FastMixer::onExit() { - delete mixer; + delete mMixer; free(mMixerBuffer); free(mSinkBuffer); } @@ -136,84 +136,84 @@ bool FastMixer::isSubClassCommand(FastThreadState::Command command) void FastMixer::onStateChange() { - const FastMixerState * const current = (const FastMixerState *) this->current; - const FastMixerState * const previous = (const FastMixerState *) this->previous; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; + const FastMixerState * const current = (const FastMixerState *) mCurrent; + const FastMixerState * const previous = (const FastMixerState *) mPrevious; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; // handle state change here, but since we want to diff the state, - // we're prepared for previous == &initial the first time through + // we're prepared for previous == &sInitial the first time through unsigned previousTrackMask; // check for change in output HAL configuration - NBAIO_Format previousFormat = format; - if (current->mOutputSinkGen != outputSinkGen) { - outputSink = current->mOutputSink; - outputSinkGen = current->mOutputSinkGen; - if (outputSink == NULL) { - format = Format_Invalid; - sampleRate = 0; + NBAIO_Format previousFormat = mFormat; + if (current->mOutputSinkGen != mOutputSinkGen) { + mOutputSink = current->mOutputSink; + mOutputSinkGen = current->mOutputSinkGen; + if (mOutputSink == NULL) { + mFormat = Format_Invalid; + mSampleRate = 0; mSinkChannelCount = 0; mSinkChannelMask = AUDIO_CHANNEL_NONE; } else { - format = outputSink->format(); - sampleRate = Format_sampleRate(format); - mSinkChannelCount = Format_channelCount(format); + mFormat = mOutputSink->format(); + mSampleRate = Format_sampleRate(mFormat); + mSinkChannelCount = Format_channelCount(mFormat); LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); // TODO: Add channel mask to NBAIO_Format // We assume that the channel mask must be a valid positional channel mask. mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); } - dumpState->mSampleRate = sampleRate; + dumpState->mSampleRate = mSampleRate; } - if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { + if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { // FIXME to avoid priority inversion, don't delete here - delete mixer; - mixer = NULL; + delete mMixer; + mMixer = NULL; free(mMixerBuffer); mMixerBuffer = NULL; free(mSinkBuffer); mSinkBuffer = NULL; - if (frameCount > 0 && sampleRate > 0) { + if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal mixer allocate for us // to avoid blocking here and to prevent possible priority inversion - mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks); + mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::kMaxFastTracks); const size_t mixerFrameSize = mSinkChannelCount * audio_bytes_per_sample(mMixerBufferFormat); mMixerBufferSize = mixerFrameSize * frameCount; (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); const size_t sinkFrameSize = mSinkChannelCount - * audio_bytes_per_sample(format.mFormat); + * audio_bytes_per_sample(mFormat.mFormat); if (sinkFrameSize > mixerFrameSize) { // need a sink buffer mSinkBufferSize = sinkFrameSize * frameCount; (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize); } - periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 - underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 - forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 - warmupNsMin = (frameCount * 750000000LL) / sampleRate; // 0.75 - warmupNsMax = (frameCount * 1250000000LL) / sampleRate; // 1.25 + mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 + mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 + mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 + mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 + mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 + mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { - periodNs = 0; - underrunNs = 0; - overrunNs = 0; - forceNs = 0; - warmupNsMin = 0; - warmupNsMax = LONG_MAX; + mPeriodNs = 0; + mUnderrunNs = 0; + mOverrunNs = 0; + mForceNs = 0; + mWarmupNsMin = 0; + mWarmupNsMax = LONG_MAX; } mMixerBufferState = UNDEFINED; #if !LOG_NDEBUG for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; + mFastTrackNames[i] = -1; } #endif // we need to reconfigure all active tracks previousTrackMask = 0; - fastTracksGen = current->mFastTracksGen - 1; + mFastTracksGen = current->mFastTracksGen - 1; dumpState->mFrameCount = frameCount; } else { previousTrackMask = previous->mTrackMask; @@ -222,7 +222,7 @@ void FastMixer::onStateChange() // check for change in active track set const unsigned currentTrackMask = current->mTrackMask; dumpState->mTrackMask = currentTrackMask; - if (current->mFastTracksGen != fastTracksGen) { + if (current->mFastTracksGen != mFastTracksGen) { ALOG_ASSERT(mMixerBuffer != NULL); int name; @@ -233,16 +233,16 @@ void FastMixer::onStateChange() removedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; ALOG_ASSERT(fastTrack->mBufferProvider == NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; + if (mMixer != NULL) { + name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); - mixer->deleteTrackName(name); + mMixer->deleteTrackName(name); } #if !LOG_NDEBUG - fastTrackNames[i] = -1; + mFastTrackNames[i] = -1; #endif // don't reset track dump state, since other side is ignoring it - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } // now process added tracks @@ -252,29 +252,29 @@ void FastMixer::onStateChange() addedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); - if (mixer != NULL) { - name = mixer->getTrackName(fastTrack->mChannelMask, + ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1); + if (mMixer != NULL) { + name = mMixer->getTrackName(fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); ALOG_ASSERT(name >= 0); - fastTrackNames[i] = name; - mixer->setBufferProvider(name, bufferProvider); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + mFastTrackNames[i] = name; + mMixer->setBufferProvider(name, bufferProvider); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer); // newly allocated track names default to full scale volume - mixer->setParameter( + mMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)(uintptr_t)fastTrack->mFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); - mixer->enable(name); + mMixer->enable(name); } - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } // finally process (potentially) modified tracks; these use the same slot @@ -284,38 +284,38 @@ void FastMixer::onStateChange() int i = __builtin_ctz(modifiedTracks); modifiedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; - if (fastTrack->mGeneration != generations[i]) { + if (fastTrack->mGeneration != mGenerations[i]) { // this track was actually modified AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; ALOG_ASSERT(bufferProvider != NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; + if (mMixer != NULL) { + name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); - mixer->setBufferProvider(name, bufferProvider); + mMixer->setBufferProvider(name, bufferProvider); if (fastTrack->mVolumeProvider == NULL) { float f = AudioMixer::UNITY_GAIN_FLOAT; - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); } - mixer->setParameter(name, AudioMixer::RESAMPLE, + mMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::REMOVE, NULL); - mixer->setParameter( + mMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)(uintptr_t)fastTrack->mFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); // already enabled } - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } } - fastTracksGen = current->mFastTracksGen; + mFastTracksGen = current->mFastTracksGen; dumpState->mNumTracks = popcount(currentTrackMask); } @@ -323,12 +323,12 @@ void FastMixer::onStateChange() void FastMixer::onWork() { - const FastMixerState * const current = (const FastMixerState *) this->current; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; - const FastMixerState::Command command = this->command; + const FastMixerState * const current = (const FastMixerState *) mCurrent; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; + const FastMixerState::Command command = mCommand; const size_t frameCount = current->mFrameCount; - if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { + if ((command & FastMixerState::MIX) && (mMixer != NULL) && mIsWarm) { ALOG_ASSERT(mMixerBuffer != NULL); // for each track, update volume and check for underrun unsigned currentTrackMask = current->mTrackMask; @@ -338,9 +338,9 @@ void FastMixer::onWork() const FastTrack* fastTrack = ¤t->mFastTracks[i]; // Refresh the per-track timestamp - if (timestampStatus == NO_ERROR) { + if (mTimestampStatus == NO_ERROR) { uint32_t trackFramesWrittenButNotPresented = - nativeFramesWrittenButNotPresented; + mNativeFramesWrittenButNotPresented; uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); // Can't provide an AudioTimestamp before first frame presented, // or during the brief 32-bit wraparound window @@ -348,20 +348,20 @@ void FastMixer::onWork() AudioTimestamp perTrackTimestamp; perTrackTimestamp.mPosition = trackFramesWritten - trackFramesWrittenButNotPresented; - perTrackTimestamp.mTime = timestamp.mTime; + perTrackTimestamp.mTime = mTimestamp.mTime; fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); } } - int name = fastTrackNames[i]; + int name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); if (fastTrack->mVolumeProvider != NULL) { gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); } // FIXME The current implementation of framesReady() for fast tracks // takes a tryLock, which can block @@ -382,43 +382,43 @@ void FastMixer::onWork() if (framesReady == 0) { underruns.mBitFields.mEmpty++; underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; - mixer->disable(name); + mMixer->disable(name); } else { // allow mixing partial buffer underruns.mBitFields.mPartial++; underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; - mixer->enable(name); + mMixer->enable(name); } } else { underruns.mBitFields.mFull++; underruns.mBitFields.mMostRecent = UNDERRUN_FULL; - mixer->enable(name); + mMixer->enable(name); } ftDump->mUnderruns = underruns; ftDump->mFramesReady = framesReady; } int64_t pts; - if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { + if (mOutputSink == NULL || (OK != mOutputSink->getNextWriteTimestamp(&pts))) { pts = AudioBufferProvider::kInvalidPTS; } // process() is CPU-bound - mixer->process(pts); + mMixer->process(pts); mMixerBufferState = MIXED; } else if (mMixerBufferState == MIXED) { mMixerBufferState = UNDEFINED; } //bool didFullWrite = false; // dumpsys could display a count of partial writes - if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) { + if ((command & FastMixerState::WRITE) && (mOutputSink != NULL) && (mMixerBuffer != NULL)) { if (mMixerBufferState == UNDEFINED) { memset(mMixerBuffer, 0, mMixerBufferSize); mMixerBufferState = ZEROED; } void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; - if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format - memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat, - frameCount * Format_channelCount(format)); + if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format + memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat, + frameCount * Format_channelCount(mFormat)); } // if non-NULL, then duplicate write() to this non-blocking sink NBAIO_Sink* teeSink; @@ -429,31 +429,31 @@ void FastMixer::onWork() // but this code should be modified to handle both non-blocking and blocking sinks dumpState->mWriteSequence++; ATRACE_BEGIN("write"); - ssize_t framesWritten = outputSink->write(buffer, frameCount); + ssize_t framesWritten = mOutputSink->write(buffer, frameCount); ATRACE_END(); dumpState->mWriteSequence++; if (framesWritten >= 0) { ALOG_ASSERT((size_t) framesWritten <= frameCount); - totalNativeFramesWritten += framesWritten; - dumpState->mFramesWritten = totalNativeFramesWritten; + mTotalNativeFramesWritten += framesWritten; + dumpState->mFramesWritten = mTotalNativeFramesWritten; //if ((size_t) framesWritten == frameCount) { // didFullWrite = true; //} } else { dumpState->mWriteErrors++; } - attemptedWrite = true; + mAttemptedWrite = true; // FIXME count # of writes blocked excessively, CPU usage, etc. for dump - timestampStatus = outputSink->getTimestamp(timestamp); - if (timestampStatus == NO_ERROR) { - uint32_t totalNativeFramesPresented = timestamp.mPosition; - if (totalNativeFramesPresented <= totalNativeFramesWritten) { - nativeFramesWrittenButNotPresented = - totalNativeFramesWritten - totalNativeFramesPresented; + mTimestampStatus = mOutputSink->getTimestamp(mTimestamp); + if (mTimestampStatus == NO_ERROR) { + uint32_t totalNativeFramesPresented = mTimestamp.mPosition; + if (totalNativeFramesPresented <= mTotalNativeFramesWritten) { + mNativeFramesWrittenButNotPresented = + mTotalNativeFramesWritten - totalNativeFramesPresented; } else { // HAL reported that more frames were presented than were written - timestampStatus = INVALID_OPERATION; + mTimestampStatus = INVALID_OPERATION; } } } diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 7649db2..06a68fb 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -48,36 +48,39 @@ private: virtual void onStateChange(); virtual void onWork(); - // FIXME these former local variables need comments and to be renamed to have "m" prefix - static const FastMixerState initial; - FastMixerState preIdle; // copy of state before we went into idle - long slopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) - int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks - int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration - NBAIO_Sink *outputSink; - int outputSinkGen; - AudioMixer* mixer; + // FIXME these former local variables need comments + static const FastMixerState sInitial; + + FastMixerState mPreIdle; // copy of state before we went into idle + long mSlopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) + int mFastTrackNames[FastMixerState::kMaxFastTracks]; + // handles used by mixer to identify tracks + int mGenerations[FastMixerState::kMaxFastTracks]; + // last observed mFastTracks[i].mGeneration + NBAIO_Sink* mOutputSink; + int mOutputSinkGen; + AudioMixer* mMixer; // mSinkBuffer audio format is stored in format.mFormat. - void* mSinkBuffer; // used for mixer output format translation + void* mSinkBuffer; // used for mixer output format translation // if sink format is different than mixer output. - size_t mSinkBufferSize; - uint32_t mSinkChannelCount; + size_t mSinkBufferSize; + uint32_t mSinkChannelCount; audio_channel_mask_t mSinkChannelMask; - void* mMixerBuffer; // mixer output buffer. - size_t mMixerBufferSize; - audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + void* mMixerBuffer; // mixer output buffer. + size_t mMixerBufferSize; + audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState; - NBAIO_Format format; - unsigned sampleRate; - int fastTracksGen; - FastMixerDumpState dummyDumpState; - uint32_t totalNativeFramesWritten; // copied to dumpState->mFramesWritten + NBAIO_Format mFormat; + unsigned mSampleRate; + int mFastTracksGen; + FastMixerDumpState mDummyFastMixerDumpState; + uint32_t mTotalNativeFramesWritten; // copied to dumpState->mFramesWritten // next 2 fields are valid only when timestampStatus == NO_ERROR - AudioTimestamp timestamp; - uint32_t nativeFramesWrittenButNotPresented; + AudioTimestamp mTimestamp; + uint32_t mNativeFramesWrittenButNotPresented; }; // class FastMixer diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index d958bf4..ac15e7c 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -55,16 +55,10 @@ private: struct FastTrackDump { FastTrackDump() : mFramesReady(0) { } /*virtual*/ ~FastTrackDump() { } - FastTrackUnderruns mUnderruns; - size_t mFramesReady; // most recent value only; no long-term statistics kept + FastTrackUnderruns mUnderruns; + size_t mFramesReady; // most recent value only; no long-term statistics kept }; -// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys. -// Each individual native word-sized field is accessed atomically. But the -// overall structure is non-atomic, that is there may be an inconsistency between fields. -// No barriers or locks are used for either writing or reading. -// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). -// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer. struct FastMixerDumpState : FastThreadDumpState { FastMixerDumpState(); /*virtual*/ ~FastMixerDumpState(); diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp index a146b9c..5ca579b 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -36,47 +36,47 @@ namespace android { FastThread::FastThread() : Thread(false /*canCallJava*/), - // re-initialized to &initial by subclass constructor - previous(NULL), current(NULL), - /* oldTs({0, 0}), */ - oldTsValid(false), - sleepNs(-1), - periodNs(0), - underrunNs(0), - overrunNs(0), - forceNs(0), - warmupNsMin(0), - warmupNsMax(LONG_MAX), - // re-initialized to &dummyDumpState by subclass constructor + // re-initialized to &sInitial by subclass constructor + mPrevious(NULL), mCurrent(NULL), + /* mOldTs({0, 0}), */ + mOldTsValid(false), + mSleepNs(-1), + mPeriodNs(0), + mUnderrunNs(0), + mOverrunNs(0), + mForceNs(0), + mWarmupNsMin(0), + mWarmupNsMax(LONG_MAX), + // re-initialized to &mDummySubclassDumpState by subclass constructor mDummyDumpState(NULL), - dumpState(NULL), - ignoreNextOverrun(true), + mDumpState(NULL), + mIgnoreNextOverrun(true), #ifdef FAST_THREAD_STATISTICS - // oldLoad - oldLoadValid(false), - bounds(0), - full(false), - // tcu + // mOldLoad + mOldLoadValid(false), + mBounds(0), + mFull(false), + // mTcu #endif - coldGen(0), - isWarm(false), - /* measuredWarmupTs({0, 0}), */ - warmupCycles(0), - warmupConsecutiveInRangeCycles(0), - // dummyLogWriter - logWriter(&dummyLogWriter), - timestampStatus(INVALID_OPERATION), + mColdGen(0), + mIsWarm(false), + /* mMeasuredWarmupTs({0, 0}), */ + mWarmupCycles(0), + mWarmupConsecutiveInRangeCycles(0), + // mDummyLogWriter + mLogWriter(&mDummyLogWriter), + mTimestampStatus(INVALID_OPERATION), - command(FastThreadState::INITIAL), + mCommand(FastThreadState::INITIAL), #if 0 frameCount(0), #endif - attemptedWrite(false) + mAttemptedWrite(false) { - oldTs.tv_sec = 0; - oldTs.tv_nsec = 0; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; + mOldTs.tv_sec = 0; + mOldTs.tv_nsec = 0; + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; } FastThread::~FastThread() @@ -88,34 +88,34 @@ bool FastThread::threadLoop() for (;;) { // either nanosleep, sched_yield, or busy wait - if (sleepNs >= 0) { - if (sleepNs > 0) { - ALOG_ASSERT(sleepNs < 1000000000); - const struct timespec req = {0, sleepNs}; + if (mSleepNs >= 0) { + if (mSleepNs > 0) { + ALOG_ASSERT(mSleepNs < 1000000000); + const struct timespec req = {0, mSleepNs}; nanosleep(&req, NULL); } else { sched_yield(); } } // default to long sleep for next cycle - sleepNs = FAST_DEFAULT_NS; + mSleepNs = FAST_DEFAULT_NS; // poll for state change const FastThreadState *next = poll(); if (next == NULL) { // continue to use the default initial state until a real state is available - // FIXME &initial not available, should save address earlier - //ALOG_ASSERT(current == &initial && previous == &initial); - next = current; + // FIXME &sInitial not available, should save address earlier + //ALOG_ASSERT(mCurrent == &sInitial && previous == &sInitial); + next = mCurrent; } - command = next->mCommand; - if (next != current) { + mCommand = next->mCommand; + if (next != mCurrent) { // As soon as possible of learning of a new dump area, start using it - dumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; - logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter; - setLog(logWriter); + mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; + mLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyLogWriter; + setLog(mLogWriter); // We want to always have a valid reference to the previous (non-idle) state. // However, the state queue only guarantees access to current and previous states. @@ -126,37 +126,38 @@ bool FastThread::threadLoop() // non-idle -> idle update previous from copy of current // idle -> idle don't update previous // idle -> non-idle don't update previous - if (!(current->mCommand & FastThreadState::IDLE)) { - if (command & FastThreadState::IDLE) { + if (!(mCurrent->mCommand & FastThreadState::IDLE)) { + if (mCommand & FastThreadState::IDLE) { onIdle(); - oldTsValid = false; + mOldTsValid = false; #ifdef FAST_THREAD_STATISTICS - oldLoadValid = false; + mOldLoadValid = false; #endif - ignoreNextOverrun = true; + mIgnoreNextOverrun = true; } - previous = current; + mPrevious = mCurrent; } - current = next; + mCurrent = next; } #if !LOG_NDEBUG next = NULL; // not referenced again #endif - dumpState->mCommand = command; + mDumpState->mCommand = mCommand; + // FIXME what does this comment mean? // << current, previous, command, dumpState >> - switch (command) { + switch (mCommand) { case FastThreadState::INITIAL: case FastThreadState::HOT_IDLE: - sleepNs = FAST_HOT_IDLE_NS; + mSleepNs = FAST_HOT_IDLE_NS; continue; case FastThreadState::COLD_IDLE: // only perform a cold idle command once // FIXME consider checking previous state and only perform if previous != COLD_IDLE - if (current->mColdGen != coldGen) { - int32_t *coldFutexAddr = current->mColdFutexAddr; + if (mCurrent->mColdGen != mColdGen) { + int32_t *coldFutexAddr = mCurrent->mColdFutexAddr; ALOG_ASSERT(coldFutexAddr != NULL); int32_t old = android_atomic_dec(coldFutexAddr); if (old <= 0) { @@ -168,42 +169,42 @@ bool FastThread::threadLoop() } // This may be overly conservative; there could be times that the normal mixer // requests such a brief cold idle that it doesn't require resetting this flag. - isWarm = false; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; - warmupCycles = 0; - warmupConsecutiveInRangeCycles = 0; - sleepNs = -1; - coldGen = current->mColdGen; + mIsWarm = false; + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; + mWarmupCycles = 0; + mWarmupConsecutiveInRangeCycles = 0; + mSleepNs = -1; + mColdGen = mCurrent->mColdGen; #ifdef FAST_THREAD_STATISTICS - bounds = 0; - full = false; + mBounds = 0; + mFull = false; #endif - oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs); - timestampStatus = INVALID_OPERATION; + mOldTsValid = !clock_gettime(CLOCK_MONOTONIC, &mOldTs); + mTimestampStatus = INVALID_OPERATION; } else { - sleepNs = FAST_HOT_IDLE_NS; + mSleepNs = FAST_HOT_IDLE_NS; } continue; case FastThreadState::EXIT: onExit(); return false; default: - LOG_ALWAYS_FATAL_IF(!isSubClassCommand(command)); + LOG_ALWAYS_FATAL_IF(!isSubClassCommand(mCommand)); break; } // there is a non-idle state available to us; did the state change? - if (current != previous) { + if (mCurrent != mPrevious) { onStateChange(); #if 1 // FIXME shouldn't need this // only process state change once - previous = current; + mPrevious = mCurrent; #endif } // do work using current state here - attemptedWrite = false; + mAttemptedWrite = false; onWork(); // To be exactly periodic, compute the next sleep time based on current time. @@ -212,13 +213,13 @@ bool FastThread::threadLoop() struct timespec newTs; int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); if (rc == 0) { - //logWriter->logTimestamp(newTs); - if (oldTsValid) { - time_t sec = newTs.tv_sec - oldTs.tv_sec; - long nsec = newTs.tv_nsec - oldTs.tv_nsec; + //mLogWriter->logTimestamp(newTs); + if (mOldTsValid) { + time_t sec = newTs.tv_sec - mOldTs.tv_sec; + long nsec = newTs.tv_nsec - mOldTs.tv_nsec; ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0), "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld", - oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); + mOldTs.tv_sec, mOldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); if (nsec < 0) { --sec; nsec += 1000000000; @@ -227,69 +228,69 @@ bool FastThread::threadLoop() // do not start pulling data from tracks and mixing until warmup is complete. // Warmup is considered complete after the earlier of: // MIN_WARMUP_CYCLES consecutive in-range write() attempts, - // where "in-range" means warmupNsMin <= cycle time <= warmupNsMax + // where "in-range" means mWarmupNsMin <= cycle time <= mWarmupNsMax // MAX_WARMUP_CYCLES write() attempts. // This is overly conservative, but to get better accuracy requires a new HAL API. - if (!isWarm && attemptedWrite) { - measuredWarmupTs.tv_sec += sec; - measuredWarmupTs.tv_nsec += nsec; - if (measuredWarmupTs.tv_nsec >= 1000000000) { - measuredWarmupTs.tv_sec++; - measuredWarmupTs.tv_nsec -= 1000000000; + if (!mIsWarm && mAttemptedWrite) { + mMeasuredWarmupTs.tv_sec += sec; + mMeasuredWarmupTs.tv_nsec += nsec; + if (mMeasuredWarmupTs.tv_nsec >= 1000000000) { + mMeasuredWarmupTs.tv_sec++; + mMeasuredWarmupTs.tv_nsec -= 1000000000; } - ++warmupCycles; - if (warmupNsMin <= nsec && nsec <= warmupNsMax) { - ALOGV("warmup cycle %d in range: %.03f ms", warmupCycles, nsec * 1e-9); - ++warmupConsecutiveInRangeCycles; + ++mWarmupCycles; + if (mWarmupNsMin <= nsec && nsec <= mWarmupNsMax) { + ALOGV("warmup cycle %d in range: %.03f ms", mWarmupCycles, nsec * 1e-9); + ++mWarmupConsecutiveInRangeCycles; } else { - ALOGV("warmup cycle %d out of range: %.03f ms", warmupCycles, nsec * 1e-9); - warmupConsecutiveInRangeCycles = 0; + ALOGV("warmup cycle %d out of range: %.03f ms", mWarmupCycles, nsec * 1e-9); + mWarmupConsecutiveInRangeCycles = 0; } - if ((warmupConsecutiveInRangeCycles >= MIN_WARMUP_CYCLES) || - (warmupCycles >= MAX_WARMUP_CYCLES)) { - isWarm = true; - dumpState->mMeasuredWarmupTs = measuredWarmupTs; - dumpState->mWarmupCycles = warmupCycles; + if ((mWarmupConsecutiveInRangeCycles >= MIN_WARMUP_CYCLES) || + (mWarmupCycles >= MAX_WARMUP_CYCLES)) { + mIsWarm = true; + mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs; + mDumpState->mWarmupCycles = mWarmupCycles; } } - sleepNs = -1; - if (isWarm) { - if (sec > 0 || nsec > underrunNs) { + mSleepNs = -1; + if (mIsWarm) { + if (sec > 0 || nsec > mUnderrunNs) { ATRACE_NAME("underrun"); // FIXME only log occasionally ALOGV("underrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); - dumpState->mUnderruns++; - ignoreNextOverrun = true; - } else if (nsec < overrunNs) { - if (ignoreNextOverrun) { - ignoreNextOverrun = false; + mDumpState->mUnderruns++; + mIgnoreNextOverrun = true; + } else if (nsec < mOverrunNs) { + if (mIgnoreNextOverrun) { + mIgnoreNextOverrun = false; } else { // FIXME only log occasionally ALOGV("overrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); - dumpState->mOverruns++; + mDumpState->mOverruns++; } // This forces a minimum cycle time. It: // - compensates for an audio HAL with jitter due to sample rate conversion // - works with a variable buffer depth audio HAL that never pulls at a - // rate < than overrunNs per buffer. + // rate < than mOverrunNs per buffer. // - recovers from overrun immediately after underrun // It doesn't work with a non-blocking audio HAL. - sleepNs = forceNs - nsec; + mSleepNs = mForceNs - nsec; } else { - ignoreNextOverrun = false; + mIgnoreNextOverrun = false; } } #ifdef FAST_THREAD_STATISTICS - if (isWarm) { + if (mIsWarm) { // advance the FIFO queue bounds - size_t i = bounds & (dumpState->mSamplingN - 1); - bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF); - if (full) { - bounds += 0x10000; - } else if (!(bounds & (dumpState->mSamplingN - 1))) { - full = true; + size_t i = mBounds & (mDumpState->mSamplingN - 1); + mBounds = (mBounds & 0xFFFF0000) | ((mBounds + 1) & 0xFFFF); + if (mFull) { + mBounds += 0x10000; + } else if (!(mBounds & (mDumpState->mSamplingN - 1))) { + mFull = true; } // compute the delta value of clock_gettime(CLOCK_MONOTONIC) uint32_t monotonicNs = nsec; @@ -301,9 +302,9 @@ bool FastThread::threadLoop() struct timespec newLoad; rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad); if (rc == 0) { - if (oldLoadValid) { - sec = newLoad.tv_sec - oldLoad.tv_sec; - nsec = newLoad.tv_nsec - oldLoad.tv_nsec; + if (mOldLoadValid) { + sec = newLoad.tv_sec - mOldLoad.tv_sec; + nsec = newLoad.tv_nsec - mOldLoad.tv_nsec; if (nsec < 0) { --sec; nsec += 1000000000; @@ -314,42 +315,42 @@ bool FastThread::threadLoop() } } else { // first time through the loop - oldLoadValid = true; + mOldLoadValid = true; } - oldLoad = newLoad; + mOldLoad = newLoad; } #ifdef CPU_FREQUENCY_STATISTICS // get the absolute value of CPU clock frequency in kHz int cpuNum = sched_getcpu(); - uint32_t kHz = tcu.getCpukHz(cpuNum); + uint32_t kHz = mTcu.getCpukHz(cpuNum); kHz = (kHz << 4) | (cpuNum & 0xF); #endif // save values in FIFO queues for dumpsys // these stores #1, #2, #3 are not atomic with respect to each other, // or with respect to store #4 below - dumpState->mMonotonicNs[i] = monotonicNs; - dumpState->mLoadNs[i] = loadNs; + mDumpState->mMonotonicNs[i] = monotonicNs; + mDumpState->mLoadNs[i] = loadNs; #ifdef CPU_FREQUENCY_STATISTICS - dumpState->mCpukHz[i] = kHz; + mDumpState->mCpukHz[i] = kHz; #endif // this store #4 is not atomic with respect to stores #1, #2, #3 above, but // the newest open & oldest closed halves are atomic with respect to each other - dumpState->mBounds = bounds; + mDumpState->mBounds = mBounds; ATRACE_INT("cycle_ms", monotonicNs / 1000000); ATRACE_INT("load_us", loadNs / 1000); } #endif } else { // first time through the loop - oldTsValid = true; - sleepNs = periodNs; - ignoreNextOverrun = true; + mOldTsValid = true; + mSleepNs = mPeriodNs; + mIgnoreNextOverrun = true; } - oldTs = newTs; + mOldTs = newTs; } else { // monotonic clock is broken - oldTsValid = false; - sleepNs = periodNs; + mOldTsValid = false; + mSleepNs = mPeriodNs; } } // for (;;) diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h index e8eaf39..2efb6de 100644 --- a/services/audioflinger/FastThread.h +++ b/services/audioflinger/FastThread.h @@ -48,44 +48,45 @@ protected: virtual void onStateChange() = 0; virtual void onWork() = 0; - // FIXME these former local variables need comments and to be renamed to have an "m" prefix - const FastThreadState *previous; - const FastThreadState *current; - struct timespec oldTs; - bool oldTsValid; - long sleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep - long periodNs; // expected period; the time required to render one mix buffer - long underrunNs; // underrun likely when write cycle is greater than this value - long overrunNs; // overrun likely when write cycle is less than this value - long forceNs; // if overrun detected, force the write cycle to take this much time - long warmupNsMin; // warmup complete when write cycle is greater than or equal to this value - long warmupNsMax; // and less than or equal to this value - FastThreadDumpState *mDummyDumpState; - FastThreadDumpState *dumpState; - bool ignoreNextOverrun; // used to ignore initial overrun and first after an underrun + // FIXME these former local variables need comments + const FastThreadState* mPrevious; + const FastThreadState* mCurrent; + struct timespec mOldTs; + bool mOldTsValid; + long mSleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep + long mPeriodNs; // expected period; the time required to render one mix buffer + long mUnderrunNs; // underrun likely when write cycle is greater than this value + long mOverrunNs; // overrun likely when write cycle is less than this value + long mForceNs; // if overrun detected, + // force the write cycle to take this much time + long mWarmupNsMin; // warmup complete when write cycle is greater than or equal to + // this value + long mWarmupNsMax; // and less than or equal to this value + FastThreadDumpState* mDummyDumpState; + FastThreadDumpState* mDumpState; + bool mIgnoreNextOverrun; // used to ignore initial overrun and first after an + // underrun #ifdef FAST_THREAD_STATISTICS - struct timespec oldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) - bool oldLoadValid; // whether oldLoad is valid - uint32_t bounds; - bool full; // whether we have collected at least mSamplingN samples + struct timespec mOldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) + bool mOldLoadValid; // whether oldLoad is valid + uint32_t mBounds; + bool mFull; // whether we have collected at least mSamplingN samples #ifdef CPU_FREQUENCY_STATISTICS - ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz + ThreadCpuUsage mTcu; // for reading the current CPU clock frequency in kHz #endif #endif - unsigned coldGen; // last observed mColdGen - bool isWarm; // true means ready to mix, false means wait for warmup before mixing - struct timespec measuredWarmupTs; // how long did it take for warmup to complete - uint32_t warmupCycles; // counter of number of loop cycles during warmup phase - uint32_t warmupConsecutiveInRangeCycles; // number of consecutive cycles in range - NBLog::Writer dummyLogWriter; - NBLog::Writer *logWriter; - status_t timestampStatus; + unsigned mColdGen; // last observed mColdGen + bool mIsWarm; // true means ready to mix, + // false means wait for warmup before mixing + struct timespec mMeasuredWarmupTs; // how long did it take for warmup to complete + uint32_t mWarmupCycles; // counter of number of loop cycles during warmup phase + uint32_t mWarmupConsecutiveInRangeCycles; // number of consecutive cycles in range + NBLog::Writer mDummyLogWriter; + NBLog::Writer* mLogWriter; + status_t mTimestampStatus; - FastThreadState::Command command; -#if 0 - size_t frameCount; -#endif - bool attemptedWrite; + FastThreadState::Command mCommand; + bool mAttemptedWrite; }; // class FastThread diff --git a/services/audioflinger/FastThreadDumpState.h b/services/audioflinger/FastThreadDumpState.h index bbca7da..1ce0914 100644 --- a/services/audioflinger/FastThreadDumpState.h +++ b/services/audioflinger/FastThreadDumpState.h @@ -22,7 +22,12 @@ namespace android { -// FIXME extract common part of comment at FastMixerDumpState +// The FastThreadDumpState keeps a cache of FastThread statistics that can be logged by dumpsys. +// Each individual native word-sized field is accessed atomically. But the +// overall structure is non-atomic, that is there may be an inconsistency between fields. +// No barriers or locks are used for either writing or reading. +// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). +// It has a different lifetime than the FastThread, and so it can't be a member of FastThread. struct FastThreadDumpState { FastThreadDumpState(); /*virtual*/ ~FastThreadDumpState(); diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp index 84a655a..7893778 100644 --- a/services/audioflinger/test-resample.cpp +++ b/services/audioflinger/test-resample.cpp @@ -427,6 +427,14 @@ int main(int argc, char* argv[]) { printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n", quality, channels, time/1000000, output_frames * looplimit / (time / 1e9) / 1e6); resampler->reset(); + + // TODO fix legacy bug: reset does not clear buffers. + // delete and recreate resampler here. + delete resampler; + resampler = AudioResampler::create(format, channels, + output_freq, quality); + resampler->setSampleRate(input_freq); + resampler->setVolume(AudioResampler::UNITY_GAIN_FLOAT, AudioResampler::UNITY_GAIN_FLOAT); } memset(output_vaddr, 0, output_size); diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh index 2c453b0..7f4d456 100755 --- a/services/audioflinger/tests/build_and_run_all_unit_tests.sh +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -15,7 +15,7 @@ mm echo "waiting for device" adb root && adb wait-for-device remount adb push $OUT/system/lib/libaudioresampler.so /system/lib -adb push $OUT/system/bin/resampler_tests /system/bin +adb push $OUT/data/nativetest/resampler_tests /system/bin sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh index e60e6d5..d0482a1 100755 --- a/services/audioflinger/tests/mixer_to_wav_tests.sh +++ b/services/audioflinger/tests/mixer_to_wav_tests.sh @@ -60,7 +60,7 @@ function createwav() { fi # Test: -# process__genericResampling +# process__genericResampling with mixed integer and float track input # track__Resample / track__genericResample adb shell test-mixer $1 -s 48000 \ -o /sdcard/tm48000grif.wav \ diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b48dc80..53ec0f6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4467,7 +4467,7 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate if (device) break; device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; if (device) break; - if (mPhoneState != AUDIO_MODE_IN_CALL) { + if (!isInCall()) { device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; |