1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "lb2/loopback_test.h" 18 19 #include <chrono> 20 #include <thread> 21 22 #include "byte_buffer.h" 23 #include "lb2/logging.h" 24 #include "lb2/util.h" 25 26 constexpr size_t LoopbackTest::COLLECTION_PERIOD_MS; 27 28 LoopbackTest::LoopbackTest(SoundSystem* soundSys, TestContext* testCtx) : 29 mSoundSys(soundSys), 30 mReadBuffer(testCtx->createAudioBuffer()), 31 mTestCtx(testCtx), 32 mRecordingFifoData(new sample_t[RECORDING_FIFO_FRAMES * testCtx->getChannelCount()]) { 33 audio_utils_fifo_init( 34 &mRecordingFifo, 35 RECORDING_FIFO_FRAMES, 36 mTestCtx->getFrameSize(), 37 mRecordingFifoData.get()); 38 } 39 40 LoopbackTest::~LoopbackTest() { 41 audio_utils_fifo_deinit(&mRecordingFifo); 42 } 43 44 bool LoopbackTest::init() { 45 return true; 46 } 47 48 int LoopbackTest::collectRecording(AudioBufferView<double> buffer) { 49 int framesRead = 0; 50 AudioBuffer<sample_t> readBuffer(mTestCtx->createAudioBuffer()); 51 52 for (size_t i = 0; i < COLLECTION_LOOPS; ++i) { 53 std::this_thread::sleep_for(std::chrono::milliseconds(COLLECTION_PERIOD_MS)); 54 if (i != 0) { 55 readBuffer.clear(); 56 } 57 while (framesRead <= static_cast<int>(buffer.getFrameCount())) { 58 // Note that we always read in mTestCtx->getFrameCount() chunks. 59 // This is how the legacy version works, but it's not clear whether 60 // this is correct, since some data from the fifo may be lost 61 // if the size of the buffer provided by Java isn't a multiple of 62 // getFrameCount(). 63 ssize_t actualFrames = audio_utils_fifo_read( 64 &mRecordingFifo, readBuffer.getData(), readBuffer.getFrameCount()); 65 if (actualFrames <= 0) break; 66 AudioBufferView<double> dst = buffer.getView(framesRead, actualFrames); 67 convertAudioBufferViewType(readBuffer.getView(0, dst.getFrameCount()), dst); 68 framesRead += actualFrames; 69 } 70 } 71 return framesRead * mTestCtx->getChannelCount(); 72 } 73 74 void LoopbackTest::receiveRecording(size_t framesRead) { 75 ssize_t actualFrames = 76 audio_utils_fifo_write(&mRecordingFifo, mReadBuffer.getData(), framesRead); 77 if (actualFrames >= 0 && static_cast<size_t>(actualFrames) != framesRead) { 78 ALOGW("recording pipe problem (expected %lld): %lld", 79 (long long)framesRead, (long long)actualFrames); 80 } else if (actualFrames < 0) { 81 ALOGW("pipe write returned negative value: %lld", (long long)actualFrames); 82 } 83 } 84 85 86 LatencyTest::LatencyTest(SoundSystem* soundSys, LatencyTestContext* testCtx) 87 : LoopbackTest(soundSys, testCtx), 88 //mTestCtx(testCtx), 89 mDrainInput(true), 90 mInputFramesToDiscard(testCtx->getInputFramesToDiscard()), 91 mInitialSilenceFrameCount(wholeMultiplier( 92 testCtx->getSamplingRateHz() * INITIAL_SILENCE_MS, MS_PER_SECOND)), 93 mInjectImpulseNextFramePos(0), 94 mImpulse(testCtx->getImpulse()) { 95 } 96 97 LatencyTest::~LatencyTest() { 98 mSoundSys->shutdown(); 99 } 100 101 bool LatencyTest::init() { 102 if (!LoopbackTest::init()) return false; 103 return mSoundSys->init(std::bind(&LatencyTest::writeCallback, this, std::placeholders::_1)); 104 } 105 106 AudioBufferView<sample_t> LatencyTest::writeCallback(size_t expectedFrames) { 107 // Always perform a read operation first since the read buffer is always 108 // filling in. But depending on the conditions, the read data is either 109 // completely discarded, or being sent to the Java layer, and may in addition 110 // be written back to the output. 111 // 112 // There are strange side effects on Pixel 2 if the app is trying to read 113 // too much data, so always read only as many frames as we can currently write. 114 // See b/68003241. 115 AudioBufferView<sample_t> readBuffer = mReadBuffer.getView(0, expectedFrames); 116 ssize_t framesRead = mSoundSys->readAudio(readBuffer); 117 // ALOGV("Read %lld frames of %lld", 118 // (long long)framesRead, (long long)readBuffer.getFrameCount()); 119 if (mInputFramesToDiscard > 0 || mInitialSilenceFrameCount > 0) { 120 if (mInputFramesToDiscard > 0) { 121 mInputFramesToDiscard -= framesRead; 122 } else { 123 if (framesRead > 0) { 124 receiveRecording(framesRead); 125 } 126 mInitialSilenceFrameCount -= expectedFrames; 127 } 128 } else if (mDrainInput) { 129 if (mSoundSys->drainInput()) { 130 mDrainInput = false; 131 } 132 } else { 133 if (framesRead > 0) { 134 receiveRecording(framesRead); 135 } 136 if (mInjectImpulseNextFramePos >= 0) { 137 ALOGV("Injecting impulse from pos %d", mInjectImpulseNextFramePos); 138 AudioBufferView<sample_t> impulseChunk = 139 mImpulse.getView(mInjectImpulseNextFramePos, expectedFrames); 140 mInjectImpulseNextFramePos += impulseChunk.getFrameCount(); 141 if (mInjectImpulseNextFramePos >= static_cast<int>(mImpulse.getFrameCount())) { 142 mInjectImpulseNextFramePos = -1; 143 } 144 return impulseChunk; 145 } else if (framesRead > 0) { 146 return readBuffer.getView(0, framesRead); 147 } 148 } 149 return AudioBuffer<sample_t>(); 150 } 151 152 153 GlitchTest::GlitchTest(SoundSystem* soundSys, GlitchTestContext* testCtx) 154 : LoopbackTest(soundSys, testCtx), 155 mTestCtx(testCtx) { 156 } 157 158 GlitchTest::~GlitchTest() { 159 mSoundSys->shutdown(); 160 } 161 162 bool GlitchTest::init() { 163 if (!LoopbackTest::init()) return false; 164 return mSoundSys->init(std::bind(&GlitchTest::writeCallback, this, std::placeholders::_1)); 165 } 166 167 AudioBufferView<sample_t> GlitchTest::writeCallback(size_t expectedFrames) { 168 ssize_t framesRead = mSoundSys->readAudio(mReadBuffer); 169 if (framesRead > 0) { 170 receiveRecording(framesRead); 171 ssize_t bbResult = byteBuffer_write( 172 reinterpret_cast<char*>(mTestCtx->getByteBuffer().getData()), 173 mTestCtx->getByteBuffer().getFrameCount(), 174 reinterpret_cast<const char*>(mReadBuffer.getData()), 175 framesRead, mTestCtx->getChannelCount()); 176 if (bbResult >= 0 && bbResult < framesRead) { 177 ALOGW("ByteBuffer only consumed %lld bytes from %lld", 178 (long long)bbResult, (long long)framesRead); 179 } else if (bbResult < 0) { 180 ALOGW("ByteBuffer error: %lld", (long long)bbResult); 181 } 182 } 183 return mTestCtx->getNextImpulse(expectedFrames); 184 } 185