Home | History | Annotate | Download | only in lb2
      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