1 /*
2  * Copyright 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 // Set to 1 for debugging race condition #1180 with mAAudioStream.
18 // See also AudioStreamAAudio.cpp in Oboe.
19 // This was left in the code so that we could test the fix again easily in the future.
20 // We could not trigger the race condition without adding these get calls and the sleeps.
21 #define DEBUG_CLOSE_RACE 0
22 
23 #include <fstream>
24 #include <iostream>
25 #if DEBUG_CLOSE_RACE
26 #include <thread>
27 #endif // DEBUG_CLOSE_RACE
28 #include <vector>
29 #include <common/AudioClock.h>
30 
31 #include <common/AudioClock.h>
32 #include "util/WaveFileWriter.h"
33 
34 #include "NativeAudioContext.h"
35 
36 using namespace oboe;
37 
convertNativeApiToAudioApi(int nativeApi)38 static oboe::AudioApi convertNativeApiToAudioApi(int nativeApi) {
39     switch (nativeApi) {
40         default:
41         case NATIVE_MODE_UNSPECIFIED:
42             return oboe::AudioApi::Unspecified;
43         case NATIVE_MODE_AAUDIO:
44             return oboe::AudioApi::AAudio;
45         case NATIVE_MODE_OPENSLES:
46             return oboe::AudioApi::OpenSLES;
47     }
48 }
49 
50 class MyOboeOutputStream : public WaveFileOutputStream {
51 public:
write(uint8_t b)52     void write(uint8_t b) override {
53         mData.push_back(b);
54     }
55 
length()56     int32_t length() {
57         return (int32_t) mData.size();
58     }
59 
getData()60     uint8_t *getData() {
61         return mData.data();
62     }
63 
64 private:
65     std::vector<uint8_t> mData;
66 };
67 
68 bool ActivityContext::mUseCallback = true;
69 int  ActivityContext::callbackSize = 0;
70 
getOutputStream()71 std::shared_ptr<oboe::AudioStream> ActivityContext::getOutputStream() {
72     for (auto entry : mOboeStreams) {
73         std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
74         if (oboeStream->getDirection() == oboe::Direction::Output) {
75             return oboeStream;
76         }
77     }
78     return nullptr;
79 }
80 
getInputStream()81 std::shared_ptr<oboe::AudioStream> ActivityContext::getInputStream() {
82     for (auto entry : mOboeStreams) {
83         std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
84         if (oboeStream != nullptr) {
85             if (oboeStream->getDirection() == oboe::Direction::Input) {
86                 return oboeStream;
87             }
88         }
89     }
90     return nullptr;
91 }
92 
freeStreamIndex(int32_t streamIndex)93 void ActivityContext::freeStreamIndex(int32_t streamIndex) {
94     mOboeStreams[streamIndex].reset();
95     mOboeStreams.erase(streamIndex);
96 }
97 
allocateStreamIndex()98 int32_t ActivityContext::allocateStreamIndex() {
99     return mNextStreamHandle++;
100 }
101 
close(int32_t streamIndex)102 void ActivityContext::close(int32_t streamIndex) {
103     stopBlockingIOThread();
104     std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
105     if (oboeStream != nullptr) {
106         oboeStream->close();
107         LOGD("ActivityContext::%s() delete stream %d ", __func__, streamIndex);
108         freeStreamIndex(streamIndex);
109     }
110 }
111 
isMMapUsed(int32_t streamIndex)112 bool ActivityContext::isMMapUsed(int32_t streamIndex) {
113     std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
114     if (oboeStream == nullptr) return false;
115     if (oboeStream->getAudioApi() != AudioApi::AAudio) return false;
116     return AAudioExtensions::getInstance().isMMapUsed(oboeStream.get());
117 }
118 
pause()119 oboe::Result ActivityContext::pause() {
120     oboe::Result result = oboe::Result::OK;
121     stopBlockingIOThread();
122     for (auto entry : mOboeStreams) {
123         std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
124         result = oboeStream->requestPause();
125     }
126     return result;
127 }
128 
stopAllStreams()129 oboe::Result ActivityContext::stopAllStreams() {
130     oboe::Result result = oboe::Result::OK;
131     stopBlockingIOThread();
132     for (auto entry : mOboeStreams) {
133         std::shared_ptr<oboe::AudioStream> oboeStream = entry.second;
134         result = oboeStream->requestStop();
135     }
136     return result;
137 }
138 
configureBuilder(bool isInput,oboe::AudioStreamBuilder & builder)139 void ActivityContext::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
140     // We needed the proxy because we did not know the channelCount when we setup the Builder.
141     if (mUseCallback) {
142         builder.setDataCallback(&oboeCallbackProxy);
143     }
144 }
145 
open(jint nativeApi,jint sampleRate,jint channelCount,jint channelMask,jint format,jint sharingMode,jint performanceMode,jint inputPreset,jint usage,jint contentType,jint deviceId,jint sessionId,jboolean channelConversionAllowed,jboolean formatConversionAllowed,jint rateConversionQuality,jboolean isMMap,jboolean isInput)146 int ActivityContext::open(jint nativeApi,
147                           jint sampleRate,
148                           jint channelCount,
149                           jint channelMask,
150                           jint format,
151                           jint sharingMode,
152                           jint performanceMode,
153                           jint inputPreset,
154                           jint usage,
155                           jint contentType,
156                           jint deviceId,
157                           jint sessionId,
158                           jboolean channelConversionAllowed,
159                           jboolean formatConversionAllowed,
160                           jint rateConversionQuality,
161                           jboolean isMMap,
162                           jboolean isInput) {
163 
164     oboe::AudioApi audioApi = oboe::AudioApi::Unspecified;
165     switch (nativeApi) {
166         case NATIVE_MODE_UNSPECIFIED:
167         case NATIVE_MODE_AAUDIO:
168         case NATIVE_MODE_OPENSLES:
169             audioApi = convertNativeApiToAudioApi(nativeApi);
170             break;
171         default:
172             return (jint) oboe::Result::ErrorOutOfRange;
173     }
174 
175     int32_t streamIndex = allocateStreamIndex();
176     if (streamIndex < 0) {
177         LOGE("ActivityContext::open() stream array full");
178         return (jint) oboe::Result::ErrorNoFreeHandles;
179     }
180 
181     if (channelCount < 0 || channelCount > 256) {
182         LOGE("ActivityContext::open() channels out of range");
183         return (jint) oboe::Result::ErrorOutOfRange;
184     }
185 
186     // Create an audio stream.
187     oboe::AudioStreamBuilder builder;
188     builder.setChannelCount(channelCount)
189             ->setDirection(isInput ? oboe::Direction::Input : oboe::Direction::Output)
190             ->setSharingMode((oboe::SharingMode) sharingMode)
191             ->setPerformanceMode((oboe::PerformanceMode) performanceMode)
192             ->setInputPreset((oboe::InputPreset)inputPreset)
193             ->setUsage((oboe::Usage)usage)
194             ->setContentType((oboe::ContentType)contentType)
195             ->setDeviceId(deviceId)
196             ->setSessionId((oboe::SessionId) sessionId)
197             ->setSampleRate(sampleRate)
198             ->setFormat((oboe::AudioFormat) format)
199             ->setChannelConversionAllowed(channelConversionAllowed)
200             ->setFormatConversionAllowed(formatConversionAllowed)
201             ->setSampleRateConversionQuality((oboe::SampleRateConversionQuality) rateConversionQuality)
202             ;
203     if (channelMask != (jint) oboe::ChannelMask::Unspecified) {
204         // Set channel mask when it is specified.
205         builder.setChannelMask((oboe::ChannelMask) channelMask);
206     }
207     if (mUseCallback) {
208         builder.setFramesPerCallback(callbackSize);
209     }
210     configureBuilder(isInput, builder);
211 
212     builder.setAudioApi(audioApi);
213 
214     // Temporarily set the AAudio MMAP policy to disable MMAP or not.
215     bool oldMMapEnabled = AAudioExtensions::getInstance().isMMapEnabled();
216     AAudioExtensions::getInstance().setMMapEnabled(isMMap);
217 
218     // Record time for opening.
219     if (isInput) {
220         mInputOpenedAt = oboe::AudioClock::getNanoseconds();
221     } else {
222         mOutputOpenedAt = oboe::AudioClock::getNanoseconds();
223     }
224     // Open a stream based on the builder settings.
225     std::shared_ptr<oboe::AudioStream> oboeStream;
226     Result result = builder.openStream(oboeStream);
227     AAudioExtensions::getInstance().setMMapEnabled(oldMMapEnabled);
228     if (result != Result::OK) {
229         freeStreamIndex(streamIndex);
230         streamIndex = -1;
231     } else {
232         mOboeStreams[streamIndex] = oboeStream; // save shared_ptr
233 
234         mChannelCount = oboeStream->getChannelCount(); // FIXME store per stream
235         mFramesPerBurst = oboeStream->getFramesPerBurst();
236         mSampleRate = oboeStream->getSampleRate();
237 
238         createRecording();
239 
240         finishOpen(isInput, oboeStream.get());
241     }
242 
243     if (!mUseCallback) {
244         int numSamples = getFramesPerBlock() * mChannelCount;
245         dataBuffer = std::make_unique<float[]>(numSamples);
246     }
247 
248     return (result != Result::OK) ? (int)result : streamIndex;
249 }
250 
start()251 oboe::Result ActivityContext::start() {
252     oboe::Result result = oboe::Result::OK;
253     std::shared_ptr<oboe::AudioStream> inputStream = getInputStream();
254     std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
255     if (inputStream == nullptr && outputStream == nullptr) {
256         LOGD("%s() - no streams defined", __func__);
257         return oboe::Result::ErrorInvalidState; // not open
258     }
259 
260     configureForStart();
261 
262     audioStreamGateway.reset();
263     result = startStreams();
264 
265     if (!mUseCallback && result == oboe::Result::OK) {
266         // Instead of using the callback, start a thread that writes the stream.
267         threadEnabled.store(true);
268         dataThread = new std::thread(threadCallback, this);
269     }
270 
271 #if DEBUG_CLOSE_RACE
272     // Also put a sleep for 400 msec in AudioStreamAAudio::updateFramesRead().
273     if (outputStream != nullptr) {
274         std::thread raceDebugger([outputStream]() {
275             while (outputStream->getState() != StreamState::Closed) {
276                 int64_t framesRead = outputStream->getFramesRead();
277                 LOGD("raceDebugger, framesRead = %d, state = %d",
278                      (int) framesRead, (int) outputStream->getState());
279             }
280         });
281         raceDebugger.detach();
282     }
283 #endif // DEBUG_CLOSE_RACE
284 
285     return result;
286 }
287 
saveWaveFile(const char * filename)288 int32_t  ActivityContext::saveWaveFile(const char *filename) {
289     if (mRecording == nullptr) {
290         LOGW("ActivityContext::saveWaveFile(%s) but no recording!", filename);
291         return -1;
292     }
293     if (mRecording->getSizeInFrames() == 0) {
294         LOGW("ActivityContext::saveWaveFile(%s) but no frames!", filename);
295         return -2;
296     }
297     MyOboeOutputStream outStream;
298     WaveFileWriter writer(&outStream);
299 
300     writer.setFrameRate(mSampleRate);
301     writer.setSamplesPerFrame(mRecording->getChannelCount());
302     writer.setBitsPerSample(24);
303     float buffer[mRecording->getChannelCount()];
304     // Read samples from start to finish.
305     mRecording->rewind();
306     for (int32_t frameIndex = 0; frameIndex < mRecording->getSizeInFrames(); frameIndex++) {
307         mRecording->read(buffer, 1 /* numFrames */);
308         for (int32_t i = 0; i < mRecording->getChannelCount(); i++) {
309             writer.write(buffer[i]);
310         }
311     }
312     writer.close();
313 
314     if (outStream.length() > 0) {
315         auto myfile = std::ofstream(filename, std::ios::out | std::ios::binary);
316         myfile.write((char *) outStream.getData(), outStream.length());
317         myfile.close();
318     }
319 
320     return outStream.length();
321 }
322 
getTimestampLatency(int32_t streamIndex)323 double ActivityContext::getTimestampLatency(int32_t streamIndex) {
324     std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex);
325     if (oboeStream != nullptr) {
326         auto result = oboeStream->calculateLatencyMillis();
327         return (!result) ? -1.0 : result.value();
328     }
329     return -1.0;
330 }
331 
332 // =================================================================== ActivityTestOutput
close(int32_t streamIndex)333 void ActivityTestOutput::close(int32_t streamIndex) {
334     ActivityContext::close(streamIndex);
335     manyToMulti.reset(nullptr);
336     monoToMulti.reset(nullptr);
337     mSinkFloat.reset();
338     mSinkI16.reset();
339     mSinkI24.reset();
340     mSinkI32.reset();
341 }
342 
setChannelEnabled(int channelIndex,bool enabled)343 void ActivityTestOutput::setChannelEnabled(int channelIndex, bool enabled) {
344     if (manyToMulti == nullptr) {
345         return;
346     }
347     if (enabled) {
348         switch (mSignalType) {
349             case SignalType::Sine:
350                 sineOscillators[channelIndex].frequency.disconnect();
351                 sineOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
352                 break;
353             case SignalType::Sawtooth:
354                 sawtoothOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
355                 break;
356             case SignalType::FreqSweep:
357                 mLinearShape.output.connect(&sineOscillators[channelIndex].frequency);
358                 sineOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
359                 break;
360             case SignalType::PitchSweep:
361                 mExponentialShape.output.connect(&sineOscillators[channelIndex].frequency);
362                 sineOscillators[channelIndex].output.connect(manyToMulti->inputs[channelIndex].get());
363                 break;
364             case SignalType::WhiteNoise:
365                 mWhiteNoise.output.connect(manyToMulti->inputs[channelIndex].get());
366                 break;
367             default:
368                 break;
369         }
370     } else {
371         manyToMulti->inputs[channelIndex]->disconnect();
372     }
373 }
374 
configureForStart()375 void ActivityTestOutput::configureForStart() {
376     manyToMulti = std::make_unique<ManyToMultiConverter>(mChannelCount);
377 
378     mSinkFloat = std::make_shared<SinkFloat>(mChannelCount);
379     mSinkI16 = std::make_shared<SinkI16>(mChannelCount);
380     mSinkI24 = std::make_shared<SinkI24>(mChannelCount);
381     mSinkI32 = std::make_shared<SinkI32>(mChannelCount);
382 
383     std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
384 
385     mTriangleOscillator.setSampleRate(outputStream->getSampleRate());
386     mTriangleOscillator.frequency.setValue(1.0/kSweepPeriod);
387     mTriangleOscillator.amplitude.setValue(1.0);
388     mTriangleOscillator.setPhase(-1.0);
389 
390     mLinearShape.setMinimum(0.0);
391     mLinearShape.setMaximum(outputStream->getSampleRate() * 0.5); // Nyquist
392 
393     mExponentialShape.setMinimum(110.0);
394     mExponentialShape.setMaximum(outputStream->getSampleRate() * 0.5); // Nyquist
395 
396     mTriangleOscillator.output.connect(&(mLinearShape.input));
397     mTriangleOscillator.output.connect(&(mExponentialShape.input));
398     {
399         double frequency = 330.0;
400         for (int i = 0; i < mChannelCount; i++) {
401             sineOscillators[i].setSampleRate(outputStream->getSampleRate());
402             sineOscillators[i].frequency.setValue(frequency);
403             sineOscillators[i].amplitude.setValue(AMPLITUDE_SINE);
404             sawtoothOscillators[i].setSampleRate(outputStream->getSampleRate());
405             sawtoothOscillators[i].frequency.setValue(frequency);
406             sawtoothOscillators[i].amplitude.setValue(AMPLITUDE_SAWTOOTH);
407 
408             frequency *= 4.0 / 3.0; // each wave is at a higher frequency
409             setChannelEnabled(i, true);
410         }
411     }
412 
413     mWhiteNoise.amplitude.setValue(0.5);
414 
415     manyToMulti->output.connect(&(mSinkFloat.get()->input));
416     manyToMulti->output.connect(&(mSinkI16.get()->input));
417     manyToMulti->output.connect(&(mSinkI24.get()->input));
418     manyToMulti->output.connect(&(mSinkI32.get()->input));
419 
420     mSinkFloat->pullReset();
421     mSinkI16->pullReset();
422     mSinkI24->pullReset();
423     mSinkI32->pullReset();
424 
425     configureStreamGateway();
426 }
427 
configureStreamGateway()428 void ActivityTestOutput::configureStreamGateway() {
429     std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
430     if (outputStream->getFormat() == oboe::AudioFormat::I16) {
431         audioStreamGateway.setAudioSink(mSinkI16);
432     } else if (outputStream->getFormat() == oboe::AudioFormat::I24) {
433         audioStreamGateway.setAudioSink(mSinkI24);
434     } else if (outputStream->getFormat() == oboe::AudioFormat::I32) {
435         audioStreamGateway.setAudioSink(mSinkI32);
436     } else if (outputStream->getFormat() == oboe::AudioFormat::Float) {
437         audioStreamGateway.setAudioSink(mSinkFloat);
438     }
439 
440     if (mUseCallback) {
441         oboeCallbackProxy.setCallback(&audioStreamGateway);
442     }
443 }
444 
runBlockingIO()445 void ActivityTestOutput::runBlockingIO() {
446     int32_t framesPerBlock = getFramesPerBlock();
447     oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue;
448 
449     std::shared_ptr<oboe::AudioStream> oboeStream = getOutputStream();
450     if (oboeStream == nullptr) {
451         LOGE("%s() : no stream found\n", __func__);
452         return;
453     }
454 
455     while (threadEnabled.load()
456            && callbackResult == oboe::DataCallbackResult::Continue) {
457         // generate output by calling the callback
458         callbackResult = audioStreamGateway.onAudioReady(oboeStream.get(),
459                                                          dataBuffer.get(),
460                                                          framesPerBlock);
461 
462         auto result = oboeStream->write(dataBuffer.get(),
463                                         framesPerBlock,
464                                         NANOS_PER_SECOND);
465 
466         if (!result) {
467             LOGE("%s() returned %s\n", __func__, convertToText(result.error()));
468             break;
469         }
470         int32_t framesWritten = result.value();
471         if (framesWritten < framesPerBlock) {
472             LOGE("%s() : write() wrote %d of %d\n", __func__, framesWritten, framesPerBlock);
473             break;
474         }
475     }
476 }
477 
478 // ======================================================================= ActivityTestInput
configureForStart()479 void ActivityTestInput::configureForStart() {
480     mInputAnalyzer.reset();
481     if (mUseCallback) {
482         oboeCallbackProxy.setCallback(&mInputAnalyzer);
483     }
484     mInputAnalyzer.setRecording(mRecording.get());
485 }
486 
runBlockingIO()487 void ActivityTestInput::runBlockingIO() {
488     int32_t framesPerBlock = getFramesPerBlock();
489     oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Continue;
490 
491     std::shared_ptr<oboe::AudioStream> oboeStream = getInputStream();
492     if (oboeStream == nullptr) {
493         LOGE("%s() : no stream found\n", __func__);
494         return;
495     }
496 
497     while (threadEnabled.load()
498            && callbackResult == oboe::DataCallbackResult::Continue) {
499 
500         // Avoid glitches by waiting until there is extra data in the FIFO.
501         auto err = oboeStream->waitForAvailableFrames(mMinimumFramesBeforeRead, kNanosPerSecond);
502         if (!err) break;
503 
504         // read from input
505         auto result = oboeStream->read(dataBuffer.get(),
506                                        framesPerBlock,
507                                        NANOS_PER_SECOND);
508         if (!result) {
509             LOGE("%s() : read() returned %s\n", __func__, convertToText(result.error()));
510             break;
511         }
512         int32_t framesRead = result.value();
513         if (framesRead < framesPerBlock) { // timeout?
514             LOGE("%s() : read() read %d of %d\n", __func__, framesRead, framesPerBlock);
515             break;
516         }
517 
518         // analyze input
519         callbackResult = mInputAnalyzer.onAudioReady(oboeStream.get(),
520                                                      dataBuffer.get(),
521                                                      framesRead);
522     }
523 }
524 
stopPlayback()525 oboe::Result ActivityRecording::stopPlayback() {
526     oboe::Result result = oboe::Result::OK;
527     if (playbackStream != nullptr) {
528         result = playbackStream->requestStop();
529         playbackStream->close();
530         mPlayRecordingCallback.setRecording(nullptr);
531         delete playbackStream;
532         playbackStream = nullptr;
533     }
534     return result;
535 }
536 
startPlayback()537 oboe::Result ActivityRecording::startPlayback() {
538     stop();
539     oboe::AudioStreamBuilder builder;
540     builder.setChannelCount(mChannelCount)
541             ->setSampleRate(mSampleRate)
542             ->setFormat(oboe::AudioFormat::Float)
543             ->setCallback(&mPlayRecordingCallback);
544     oboe::Result result = builder.openStream(&playbackStream);
545     if (result != oboe::Result::OK) {
546         delete playbackStream;
547         playbackStream = nullptr;
548     } else if (playbackStream != nullptr) {
549         if (mRecording != nullptr) {
550             mRecording->rewind();
551             mPlayRecordingCallback.setRecording(mRecording.get());
552             result = playbackStream->requestStart();
553         }
554     }
555     return result;
556 }
557 
558 // ======================================================================= ActivityTapToTone
configureForStart()559 void ActivityTapToTone::configureForStart() {
560     monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
561 
562     mSinkFloat = std::make_shared<SinkFloat>(mChannelCount);
563     mSinkI16 = std::make_shared<SinkI16>(mChannelCount);
564     mSinkI24 = std::make_shared<SinkI24>(mChannelCount);
565     mSinkI32 = std::make_shared<SinkI32>(mChannelCount);
566 
567     std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
568     sawPingGenerator.setSampleRate(outputStream->getSampleRate());
569     sawPingGenerator.frequency.setValue(FREQUENCY_SAW_PING);
570     sawPingGenerator.amplitude.setValue(AMPLITUDE_SAW_PING);
571 
572     sawPingGenerator.output.connect(&(monoToMulti->input));
573     monoToMulti->output.connect(&(mSinkFloat.get()->input));
574     monoToMulti->output.connect(&(mSinkI16.get()->input));
575     monoToMulti->output.connect(&(mSinkI24.get()->input));
576     monoToMulti->output.connect(&(mSinkI32.get()->input));
577 
578     mSinkFloat->pullReset();
579     mSinkI16->pullReset();
580     mSinkI24->pullReset();
581     mSinkI32->pullReset();
582 
583     configureStreamGateway();
584 }
585 
586 // ======================================================================= ActivityFullDuplex
configureBuilder(bool isInput,oboe::AudioStreamBuilder & builder)587 void ActivityFullDuplex::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
588     if (isInput) {
589         // Ideally the output streams should be opened first.
590         std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
591         if (outputStream != nullptr) {
592             // The input and output buffers will run in sync with input empty
593             // and output full. So set the input capacity to match the output.
594             builder.setBufferCapacityInFrames(outputStream->getBufferCapacityInFrames());
595         }
596     }
597 }
598 
599 // ======================================================================= ActivityEcho
configureBuilder(bool isInput,oboe::AudioStreamBuilder & builder)600 void ActivityEcho::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
601     ActivityFullDuplex::configureBuilder(isInput, builder);
602 
603     if (mFullDuplexEcho.get() == nullptr) {
604         mFullDuplexEcho = std::make_unique<FullDuplexEcho>();
605     }
606     // only output uses a callback, input is polled
607     if (!isInput) {
608         builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
609         oboeCallbackProxy.setCallback(mFullDuplexEcho.get());
610     }
611 }
612 
finishOpen(bool isInput,oboe::AudioStream * oboeStream)613 void ActivityEcho::finishOpen(bool isInput, oboe::AudioStream *oboeStream) {
614     if (isInput) {
615         mFullDuplexEcho->setInputStream(oboeStream);
616     } else {
617         mFullDuplexEcho->setOutputStream(oboeStream);
618     }
619 }
620 
621 // ======================================================================= ActivityRoundTripLatency
configureBuilder(bool isInput,oboe::AudioStreamBuilder & builder)622 void ActivityRoundTripLatency::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
623     ActivityFullDuplex::configureBuilder(isInput, builder);
624 
625     if (mFullDuplexLatency.get() == nullptr) {
626         mFullDuplexLatency = std::make_unique<FullDuplexAnalyzer>(mLatencyAnalyzer.get());
627     }
628     if (!isInput) {
629         // only output uses a callback, input is polled
630         builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
631         oboeCallbackProxy.setCallback(mFullDuplexLatency.get());
632     }
633 }
634 
finishOpen(bool isInput,AudioStream * oboeStream)635 void ActivityRoundTripLatency::finishOpen(bool isInput, AudioStream *oboeStream) {
636     if (isInput) {
637         mFullDuplexLatency->setInputStream(oboeStream);
638         mFullDuplexLatency->setRecording(mRecording.get());
639     } else {
640         mFullDuplexLatency->setOutputStream(oboeStream);
641     }
642 }
643 
644 // The timestamp latency is the difference between the input
645 // and output times for a specific frame.
646 // Start with the position and time from an input timestamp.
647 // Map the input position to the corresponding position in output
648 // and calculate its time.
649 // Use the difference between framesWritten and framesRead to
650 // convert input positions to output positions.
measureTimestampLatency()651 jdouble ActivityRoundTripLatency::measureTimestampLatency() {
652     if (!mFullDuplexLatency->isWriteReadDeltaValid()) return -1.0;
653 
654     int64_t writeReadDelta = mFullDuplexLatency->getWriteReadDelta();
655     auto inputTimestampResult = mFullDuplexLatency->getInputStream()->getTimestamp(CLOCK_MONOTONIC);
656     if (!inputTimestampResult) return -1.0;
657     auto outputTimestampResult = mFullDuplexLatency->getOutputStream()->getTimestamp(CLOCK_MONOTONIC);
658     if (!outputTimestampResult) return -1.0;
659 
660     int64_t inputPosition = inputTimestampResult.value().position;
661     int64_t inputTimeNanos = inputTimestampResult.value().timestamp;
662     int64_t ouputPosition = outputTimestampResult.value().position;
663     int64_t outputTimeNanos = outputTimestampResult.value().timestamp;
664 
665     // Map input frame position to the corresponding output frame.
666     int64_t mappedPosition = inputPosition + writeReadDelta;
667     // Calculate when that frame will play.
668     int32_t sampleRate = mFullDuplexLatency->getOutputStream()->getSampleRate();
669     int64_t mappedTimeNanos = outputTimeNanos + ((mappedPosition - ouputPosition) * 1e9) / sampleRate;
670 
671     // Latency is the difference in time between when a frame was recorded and
672     // when its corresponding echo was played.
673     return (mappedTimeNanos - inputTimeNanos) * 1.0e-6; // convert nanos to millis
674 }
675 
676 // ======================================================================= ActivityGlitches
configureBuilder(bool isInput,oboe::AudioStreamBuilder & builder)677 void ActivityGlitches::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
678     ActivityFullDuplex::configureBuilder(isInput, builder);
679 
680     if (mFullDuplexGlitches.get() == nullptr) {
681         mFullDuplexGlitches = std::make_unique<FullDuplexAnalyzer>(&mGlitchAnalyzer);
682     }
683     if (!isInput) {
684         // only output uses a callback, input is polled
685         builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
686         oboeCallbackProxy.setCallback(mFullDuplexGlitches.get());
687     }
688 }
689 
finishOpen(bool isInput,oboe::AudioStream * oboeStream)690 void ActivityGlitches::finishOpen(bool isInput, oboe::AudioStream *oboeStream) {
691     if (isInput) {
692         mFullDuplexGlitches->setInputStream(oboeStream);
693         mFullDuplexGlitches->setRecording(mRecording.get());
694     } else {
695         mFullDuplexGlitches->setOutputStream(oboeStream);
696     }
697 }
698 
699 // ======================================================================= ActivityDataPath
configureBuilder(bool isInput,oboe::AudioStreamBuilder & builder)700 void ActivityDataPath::configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) {
701     ActivityFullDuplex::configureBuilder(isInput, builder);
702 
703     if (mFullDuplexDataPath.get() == nullptr) {
704         mFullDuplexDataPath = std::make_unique<FullDuplexAnalyzer>(&mDataPathAnalyzer);
705     }
706     if (!isInput) {
707         // only output uses a callback, input is polled
708         builder.setCallback((oboe::AudioStreamCallback *) &oboeCallbackProxy);
709         oboeCallbackProxy.setCallback(mFullDuplexDataPath.get());
710     }
711 }
712 
finishOpen(bool isInput,oboe::AudioStream * oboeStream)713 void ActivityDataPath::finishOpen(bool isInput, oboe::AudioStream *oboeStream) {
714     if (isInput) {
715         mFullDuplexDataPath->setInputStream(oboeStream);
716         mFullDuplexDataPath->setRecording(mRecording.get());
717     } else {
718         mFullDuplexDataPath->setOutputStream(oboeStream);
719     }
720 }
721 
722 // =================================================================== ActivityTestDisconnect
close(int32_t streamIndex)723 void ActivityTestDisconnect::close(int32_t streamIndex) {
724     ActivityContext::close(streamIndex);
725     mSinkFloat.reset();
726 }
727 
configureForStart()728 void ActivityTestDisconnect::configureForStart() {
729     std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream();
730     std::shared_ptr<oboe::AudioStream> inputStream = getInputStream();
731     if (outputStream) {
732         mSinkFloat = std::make_unique<SinkFloat>(mChannelCount);
733         sineOscillator = std::make_unique<SineOscillator>();
734         monoToMulti = std::make_unique<MonoToMultiConverter>(mChannelCount);
735 
736         sineOscillator->setSampleRate(outputStream->getSampleRate());
737         sineOscillator->frequency.setValue(440.0);
738         sineOscillator->amplitude.setValue(AMPLITUDE_SINE);
739         sineOscillator->output.connect(&(monoToMulti->input));
740 
741         monoToMulti->output.connect(&(mSinkFloat->input));
742         mSinkFloat->pullReset();
743         audioStreamGateway.setAudioSink(mSinkFloat);
744     } else if (inputStream) {
745         audioStreamGateway.setAudioSink(nullptr);
746     }
747     oboeCallbackProxy.setCallback(&audioStreamGateway);
748 }
749 
750