• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LoopbackTest(SoundSystem * soundSys,TestContext * testCtx)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 
~LoopbackTest()40 LoopbackTest::~LoopbackTest() {
41     audio_utils_fifo_deinit(&mRecordingFifo);
42 }
43 
init()44 bool LoopbackTest::init() {
45     return true;
46 }
47 
collectRecording(AudioBufferView<double> buffer)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 
receiveRecording(size_t framesRead)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 
LatencyTest(SoundSystem * soundSys,LatencyTestContext * testCtx)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 
~LatencyTest()97 LatencyTest::~LatencyTest() {
98     mSoundSys->shutdown();
99 }
100 
init()101 bool LatencyTest::init() {
102     if (!LoopbackTest::init()) return false;
103     return mSoundSys->init(std::bind(&LatencyTest::writeCallback, this, std::placeholders::_1));
104 }
105 
writeCallback(size_t expectedFrames)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 
GlitchTest(SoundSystem * soundSys,GlitchTestContext * testCtx)153 GlitchTest::GlitchTest(SoundSystem* soundSys, GlitchTestContext* testCtx)
154         : LoopbackTest(soundSys, testCtx),
155           mTestCtx(testCtx) {
156 }
157 
~GlitchTest()158 GlitchTest::~GlitchTest() {
159     mSoundSys->shutdown();
160 }
161 
init()162 bool GlitchTest::init() {
163     if (!LoopbackTest::init()) return false;
164     return mSoundSys->init(std::bind(&GlitchTest::writeCallback, this, std::placeholders::_1));
165 }
166 
writeCallback(size_t expectedFrames)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