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