xref: /aosp_15_r20/external/webrtc/sdk/objc/unittests/RTCAudioDeviceModule_xctest.mm (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker/*
2*d9f75844SAndroid Build Coastguard Worker *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker#import <XCTest/XCTest.h>
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker#if defined(WEBRTC_IOS)
14*d9f75844SAndroid Build Coastguard Worker#import "sdk/objc/native/api/audio_device_module.h"
15*d9f75844SAndroid Build Coastguard Worker#endif
16*d9f75844SAndroid Build Coastguard Worker
17*d9f75844SAndroid Build Coastguard Worker#include "api/scoped_refptr.h"
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Workertypedef int32_t(^NeedMorePlayDataBlock)(const size_t nSamples,
20*d9f75844SAndroid Build Coastguard Worker                                        const size_t nBytesPerSample,
21*d9f75844SAndroid Build Coastguard Worker                                        const size_t nChannels,
22*d9f75844SAndroid Build Coastguard Worker                                        const uint32_t samplesPerSec,
23*d9f75844SAndroid Build Coastguard Worker                                        void* audioSamples,
24*d9f75844SAndroid Build Coastguard Worker                                        size_t& nSamplesOut,
25*d9f75844SAndroid Build Coastguard Worker                                        int64_t* elapsed_time_ms,
26*d9f75844SAndroid Build Coastguard Worker                                        int64_t* ntp_time_ms);
27*d9f75844SAndroid Build Coastguard Worker
28*d9f75844SAndroid Build Coastguard Workertypedef int32_t(^RecordedDataIsAvailableBlock)(const void* audioSamples,
29*d9f75844SAndroid Build Coastguard Worker                                               const size_t nSamples,
30*d9f75844SAndroid Build Coastguard Worker                                               const size_t nBytesPerSample,
31*d9f75844SAndroid Build Coastguard Worker                                               const size_t nChannels,
32*d9f75844SAndroid Build Coastguard Worker                                               const uint32_t samplesPerSec,
33*d9f75844SAndroid Build Coastguard Worker                                               const uint32_t totalDelayMS,
34*d9f75844SAndroid Build Coastguard Worker                                               const int32_t clockDrift,
35*d9f75844SAndroid Build Coastguard Worker                                               const uint32_t currentMicLevel,
36*d9f75844SAndroid Build Coastguard Worker                                               const bool keyPressed,
37*d9f75844SAndroid Build Coastguard Worker                                               uint32_t& newMicLevel);
38*d9f75844SAndroid Build Coastguard Worker
39*d9f75844SAndroid Build Coastguard Worker
40*d9f75844SAndroid Build Coastguard Worker// This class implements the AudioTransport API and forwards all methods to the appropriate blocks.
41*d9f75844SAndroid Build Coastguard Workerclass MockAudioTransport : public webrtc::AudioTransport {
42*d9f75844SAndroid Build Coastguard Workerpublic:
43*d9f75844SAndroid Build Coastguard Worker  MockAudioTransport() {}
44*d9f75844SAndroid Build Coastguard Worker  ~MockAudioTransport() override {}
45*d9f75844SAndroid Build Coastguard Worker
46*d9f75844SAndroid Build Coastguard Worker  void expectNeedMorePlayData(NeedMorePlayDataBlock block) {
47*d9f75844SAndroid Build Coastguard Worker    needMorePlayDataBlock = block;
48*d9f75844SAndroid Build Coastguard Worker  }
49*d9f75844SAndroid Build Coastguard Worker
50*d9f75844SAndroid Build Coastguard Worker  void expectRecordedDataIsAvailable(RecordedDataIsAvailableBlock block) {
51*d9f75844SAndroid Build Coastguard Worker    recordedDataIsAvailableBlock = block;
52*d9f75844SAndroid Build Coastguard Worker  }
53*d9f75844SAndroid Build Coastguard Worker
54*d9f75844SAndroid Build Coastguard Worker  int32_t NeedMorePlayData(const size_t nSamples,
55*d9f75844SAndroid Build Coastguard Worker                           const size_t nBytesPerSample,
56*d9f75844SAndroid Build Coastguard Worker                           const size_t nChannels,
57*d9f75844SAndroid Build Coastguard Worker                           const uint32_t samplesPerSec,
58*d9f75844SAndroid Build Coastguard Worker                           void* audioSamples,
59*d9f75844SAndroid Build Coastguard Worker                           size_t& nSamplesOut,
60*d9f75844SAndroid Build Coastguard Worker                           int64_t* elapsed_time_ms,
61*d9f75844SAndroid Build Coastguard Worker                           int64_t* ntp_time_ms) override {
62*d9f75844SAndroid Build Coastguard Worker    return needMorePlayDataBlock(nSamples,
63*d9f75844SAndroid Build Coastguard Worker                                 nBytesPerSample,
64*d9f75844SAndroid Build Coastguard Worker                                 nChannels,
65*d9f75844SAndroid Build Coastguard Worker                                 samplesPerSec,
66*d9f75844SAndroid Build Coastguard Worker                                 audioSamples,
67*d9f75844SAndroid Build Coastguard Worker                                 nSamplesOut,
68*d9f75844SAndroid Build Coastguard Worker                                 elapsed_time_ms,
69*d9f75844SAndroid Build Coastguard Worker                                 ntp_time_ms);
70*d9f75844SAndroid Build Coastguard Worker  }
71*d9f75844SAndroid Build Coastguard Worker
72*d9f75844SAndroid Build Coastguard Worker  int32_t RecordedDataIsAvailable(const void* audioSamples,
73*d9f75844SAndroid Build Coastguard Worker                                  const size_t nSamples,
74*d9f75844SAndroid Build Coastguard Worker                                  const size_t nBytesPerSample,
75*d9f75844SAndroid Build Coastguard Worker                                  const size_t nChannels,
76*d9f75844SAndroid Build Coastguard Worker                                  const uint32_t samplesPerSec,
77*d9f75844SAndroid Build Coastguard Worker                                  const uint32_t totalDelayMS,
78*d9f75844SAndroid Build Coastguard Worker                                  const int32_t clockDrift,
79*d9f75844SAndroid Build Coastguard Worker                                  const uint32_t currentMicLevel,
80*d9f75844SAndroid Build Coastguard Worker                                  const bool keyPressed,
81*d9f75844SAndroid Build Coastguard Worker                                  uint32_t& newMicLevel) override {
82*d9f75844SAndroid Build Coastguard Worker    return recordedDataIsAvailableBlock(audioSamples,
83*d9f75844SAndroid Build Coastguard Worker                                        nSamples,
84*d9f75844SAndroid Build Coastguard Worker                                        nBytesPerSample,
85*d9f75844SAndroid Build Coastguard Worker                                        nChannels,
86*d9f75844SAndroid Build Coastguard Worker                                        samplesPerSec,
87*d9f75844SAndroid Build Coastguard Worker                                        totalDelayMS,
88*d9f75844SAndroid Build Coastguard Worker                                        clockDrift,
89*d9f75844SAndroid Build Coastguard Worker                                        currentMicLevel,
90*d9f75844SAndroid Build Coastguard Worker                                        keyPressed,
91*d9f75844SAndroid Build Coastguard Worker                                        newMicLevel);
92*d9f75844SAndroid Build Coastguard Worker  }
93*d9f75844SAndroid Build Coastguard Worker
94*d9f75844SAndroid Build Coastguard Worker  void PullRenderData(int bits_per_sample,
95*d9f75844SAndroid Build Coastguard Worker                      int sample_rate,
96*d9f75844SAndroid Build Coastguard Worker                      size_t number_of_channels,
97*d9f75844SAndroid Build Coastguard Worker                      size_t number_of_frames,
98*d9f75844SAndroid Build Coastguard Worker                      void* audio_data,
99*d9f75844SAndroid Build Coastguard Worker                      int64_t* elapsed_time_ms,
100*d9f75844SAndroid Build Coastguard Worker                      int64_t* ntp_time_ms) override {}
101*d9f75844SAndroid Build Coastguard Worker
102*d9f75844SAndroid Build Coastguard Worker private:
103*d9f75844SAndroid Build Coastguard Worker  NeedMorePlayDataBlock needMorePlayDataBlock;
104*d9f75844SAndroid Build Coastguard Worker  RecordedDataIsAvailableBlock recordedDataIsAvailableBlock;
105*d9f75844SAndroid Build Coastguard Worker};
106*d9f75844SAndroid Build Coastguard Worker
107*d9f75844SAndroid Build Coastguard Worker// Number of callbacks (input or output) the tests waits for before we set
108*d9f75844SAndroid Build Coastguard Worker// an event indicating that the test was OK.
109*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kNumCallbacks = 10;
110*d9f75844SAndroid Build Coastguard Worker// Max amount of time we wait for an event to be set while counting callbacks.
111*d9f75844SAndroid Build Coastguard Workerstatic const NSTimeInterval kTestTimeOutInSec = 20.0;
112*d9f75844SAndroid Build Coastguard Worker// Number of bits per PCM audio sample.
113*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kBitsPerSample = 16;
114*d9f75844SAndroid Build Coastguard Worker// Number of bytes per PCM audio sample.
115*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kBytesPerSample = kBitsPerSample / 8;
116*d9f75844SAndroid Build Coastguard Worker// Average number of audio callbacks per second assuming 10ms packet size.
117*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kNumCallbacksPerSecond = 100;
118*d9f75844SAndroid Build Coastguard Worker// Play out a test file during this time (unit is in seconds).
119*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kFilePlayTimeInSec = 15;
120*d9f75844SAndroid Build Coastguard Worker// Run the full-duplex test during this time (unit is in seconds).
121*d9f75844SAndroid Build Coastguard Worker// Note that first `kNumIgnoreFirstCallbacks` are ignored.
122*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kFullDuplexTimeInSec = 10;
123*d9f75844SAndroid Build Coastguard Worker// Wait for the callback sequence to stabilize by ignoring this amount of the
124*d9f75844SAndroid Build Coastguard Worker// initial callbacks (avoids initial FIFO access).
125*d9f75844SAndroid Build Coastguard Worker// Only used in the RunPlayoutAndRecordingInFullDuplex test.
126*d9f75844SAndroid Build Coastguard Workerstatic const NSUInteger kNumIgnoreFirstCallbacks = 50;
127*d9f75844SAndroid Build Coastguard Worker
128*d9f75844SAndroid Build Coastguard Worker@interface RTCAudioDeviceModuleTests : XCTestCase {
129*d9f75844SAndroid Build Coastguard Worker  rtc::scoped_refptr<webrtc::AudioDeviceModule> audioDeviceModule;
130*d9f75844SAndroid Build Coastguard Worker  MockAudioTransport mock;
131*d9f75844SAndroid Build Coastguard Worker}
132*d9f75844SAndroid Build Coastguard Worker
133*d9f75844SAndroid Build Coastguard Worker@property(nonatomic, assign) webrtc::AudioParameters playoutParameters;
134*d9f75844SAndroid Build Coastguard Worker@property(nonatomic, assign) webrtc::AudioParameters recordParameters;
135*d9f75844SAndroid Build Coastguard Worker
136*d9f75844SAndroid Build Coastguard Worker@end
137*d9f75844SAndroid Build Coastguard Worker
138*d9f75844SAndroid Build Coastguard Worker@implementation RTCAudioDeviceModuleTests
139*d9f75844SAndroid Build Coastguard Worker
140*d9f75844SAndroid Build Coastguard Worker@synthesize playoutParameters;
141*d9f75844SAndroid Build Coastguard Worker@synthesize recordParameters;
142*d9f75844SAndroid Build Coastguard Worker
143*d9f75844SAndroid Build Coastguard Worker- (void)setUp {
144*d9f75844SAndroid Build Coastguard Worker  [super setUp];
145*d9f75844SAndroid Build Coastguard Worker  audioDeviceModule = webrtc::CreateAudioDeviceModule();
146*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->Init());
147*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->GetPlayoutAudioParameters(&playoutParameters));
148*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->GetRecordAudioParameters(&recordParameters));
149*d9f75844SAndroid Build Coastguard Worker}
150*d9f75844SAndroid Build Coastguard Worker
151*d9f75844SAndroid Build Coastguard Worker- (void)tearDown {
152*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->Terminate());
153*d9f75844SAndroid Build Coastguard Worker  audioDeviceModule = nullptr;
154*d9f75844SAndroid Build Coastguard Worker  [super tearDown];
155*d9f75844SAndroid Build Coastguard Worker}
156*d9f75844SAndroid Build Coastguard Worker
157*d9f75844SAndroid Build Coastguard Worker- (void)startPlayout {
158*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(audioDeviceModule->Playing());
159*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->InitPlayout());
160*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(audioDeviceModule->PlayoutIsInitialized());
161*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->StartPlayout());
162*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(audioDeviceModule->Playing());
163*d9f75844SAndroid Build Coastguard Worker}
164*d9f75844SAndroid Build Coastguard Worker
165*d9f75844SAndroid Build Coastguard Worker- (void)stopPlayout {
166*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->StopPlayout());
167*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(audioDeviceModule->Playing());
168*d9f75844SAndroid Build Coastguard Worker}
169*d9f75844SAndroid Build Coastguard Worker
170*d9f75844SAndroid Build Coastguard Worker- (void)startRecording{
171*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(audioDeviceModule->Recording());
172*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->InitRecording());
173*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(audioDeviceModule->RecordingIsInitialized());
174*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->StartRecording());
175*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(audioDeviceModule->Recording());
176*d9f75844SAndroid Build Coastguard Worker}
177*d9f75844SAndroid Build Coastguard Worker
178*d9f75844SAndroid Build Coastguard Worker- (void)stopRecording{
179*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->StopRecording());
180*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(audioDeviceModule->Recording());
181*d9f75844SAndroid Build Coastguard Worker}
182*d9f75844SAndroid Build Coastguard Worker
183*d9f75844SAndroid Build Coastguard Worker- (NSURL*)fileURLForSampleRate:(int)sampleRate {
184*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(sampleRate == 48000 || sampleRate == 44100 || sampleRate == 16000);
185*d9f75844SAndroid Build Coastguard Worker  NSString *filename = [NSString stringWithFormat:@"audio_short%d", sampleRate / 1000];
186*d9f75844SAndroid Build Coastguard Worker  NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:@"pcm"];
187*d9f75844SAndroid Build Coastguard Worker  XCTAssertNotNil(url);
188*d9f75844SAndroid Build Coastguard Worker
189*d9f75844SAndroid Build Coastguard Worker  return url;
190*d9f75844SAndroid Build Coastguard Worker}
191*d9f75844SAndroid Build Coastguard Worker
192*d9f75844SAndroid Build Coastguard Worker#pragma mark - Tests
193*d9f75844SAndroid Build Coastguard Worker
194*d9f75844SAndroid Build Coastguard Worker- (void)testConstructDestruct {
195*d9f75844SAndroid Build Coastguard Worker  // Using the test fixture to create and destruct the audio device module.
196*d9f75844SAndroid Build Coastguard Worker}
197*d9f75844SAndroid Build Coastguard Worker
198*d9f75844SAndroid Build Coastguard Worker- (void)testInitTerminate {
199*d9f75844SAndroid Build Coastguard Worker  // Initialization is part of the test fixture.
200*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(audioDeviceModule->Initialized());
201*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->Terminate());
202*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(audioDeviceModule->Initialized());
203*d9f75844SAndroid Build Coastguard Worker}
204*d9f75844SAndroid Build Coastguard Worker
205*d9f75844SAndroid Build Coastguard Worker// Tests that playout can be initiated, started and stopped. No audio callback
206*d9f75844SAndroid Build Coastguard Worker// is registered in this test.
207*d9f75844SAndroid Build Coastguard Worker- (void)testStartStopPlayout {
208*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
209*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
210*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
211*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
212*d9f75844SAndroid Build Coastguard Worker}
213*d9f75844SAndroid Build Coastguard Worker
214*d9f75844SAndroid Build Coastguard Worker// Tests that recording can be initiated, started and stopped. No audio callback
215*d9f75844SAndroid Build Coastguard Worker// is registered in this test.
216*d9f75844SAndroid Build Coastguard Worker- (void)testStartStopRecording {
217*d9f75844SAndroid Build Coastguard Worker  [self startRecording];
218*d9f75844SAndroid Build Coastguard Worker  [self stopRecording];
219*d9f75844SAndroid Build Coastguard Worker  [self startRecording];
220*d9f75844SAndroid Build Coastguard Worker  [self stopRecording];
221*d9f75844SAndroid Build Coastguard Worker}
222*d9f75844SAndroid Build Coastguard Worker// Verify that calling StopPlayout() will leave us in an uninitialized state
223*d9f75844SAndroid Build Coastguard Worker// which will require a new call to InitPlayout(). This test does not call
224*d9f75844SAndroid Build Coastguard Worker// StartPlayout() while being uninitialized since doing so will hit a
225*d9f75844SAndroid Build Coastguard Worker// RTC_DCHECK.
226*d9f75844SAndroid Build Coastguard Worker- (void)testStopPlayoutRequiresInitToRestart {
227*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->InitPlayout());
228*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->StartPlayout());
229*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->StopPlayout());
230*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(audioDeviceModule->PlayoutIsInitialized());
231*d9f75844SAndroid Build Coastguard Worker}
232*d9f75844SAndroid Build Coastguard Worker
233*d9f75844SAndroid Build Coastguard Worker// Verify that we can create two ADMs and start playing on the second ADM.
234*d9f75844SAndroid Build Coastguard Worker// Only the first active instance shall activate an audio session and the
235*d9f75844SAndroid Build Coastguard Worker// last active instance shall deactivate the audio session. The test does not
236*d9f75844SAndroid Build Coastguard Worker// explicitly verify correct audio session calls but instead focuses on
237*d9f75844SAndroid Build Coastguard Worker// ensuring that audio starts for both ADMs.
238*d9f75844SAndroid Build Coastguard Worker- (void)testStartPlayoutOnTwoInstances {
239*d9f75844SAndroid Build Coastguard Worker  // Create and initialize a second/extra ADM instance. The default ADM is
240*d9f75844SAndroid Build Coastguard Worker  // created by the test harness.
241*d9f75844SAndroid Build Coastguard Worker  rtc::scoped_refptr<webrtc::AudioDeviceModule> secondAudioDeviceModule =
242*d9f75844SAndroid Build Coastguard Worker      webrtc::CreateAudioDeviceModule();
243*d9f75844SAndroid Build Coastguard Worker  XCTAssertNotEqual(secondAudioDeviceModule.get(), nullptr);
244*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, secondAudioDeviceModule->Init());
245*d9f75844SAndroid Build Coastguard Worker
246*d9f75844SAndroid Build Coastguard Worker  // Start playout for the default ADM but don't wait here. Instead use the
247*d9f75844SAndroid Build Coastguard Worker  // upcoming second stream for that. We set the same expectation on number
248*d9f75844SAndroid Build Coastguard Worker  // of callbacks as for the second stream.
249*d9f75844SAndroid Build Coastguard Worker  mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
250*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
251*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
252*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
253*d9f75844SAndroid Build Coastguard Worker                                       void *audioSamples,
254*d9f75844SAndroid Build Coastguard Worker                                       size_t &nSamplesOut,
255*d9f75844SAndroid Build Coastguard Worker                                       int64_t *elapsed_time_ms,
256*d9f75844SAndroid Build Coastguard Worker                                       int64_t *ntp_time_ms) {
257*d9f75844SAndroid Build Coastguard Worker    nSamplesOut = nSamples;
258*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer());
259*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nBytesPerSample, kBytesPerSample);
260*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nChannels, self.playoutParameters.channels());
261*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate());
262*d9f75844SAndroid Build Coastguard Worker    XCTAssertNotEqual((void*)NULL, audioSamples);
263*d9f75844SAndroid Build Coastguard Worker
264*d9f75844SAndroid Build Coastguard Worker    return 0;
265*d9f75844SAndroid Build Coastguard Worker  });
266*d9f75844SAndroid Build Coastguard Worker
267*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
268*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
269*d9f75844SAndroid Build Coastguard Worker
270*d9f75844SAndroid Build Coastguard Worker  // Initialize playout for the second ADM. If all is OK, the second ADM shall
271*d9f75844SAndroid Build Coastguard Worker  // reuse the audio session activated when the first ADM started playing.
272*d9f75844SAndroid Build Coastguard Worker  // This call will also ensure that we avoid a problem related to initializing
273*d9f75844SAndroid Build Coastguard Worker  // two different audio unit instances back to back (see webrtc:5166 for
274*d9f75844SAndroid Build Coastguard Worker  // details).
275*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, secondAudioDeviceModule->InitPlayout());
276*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(secondAudioDeviceModule->PlayoutIsInitialized());
277*d9f75844SAndroid Build Coastguard Worker
278*d9f75844SAndroid Build Coastguard Worker  // Start playout for the second ADM and verify that it starts as intended.
279*d9f75844SAndroid Build Coastguard Worker  // Passing this test ensures that initialization of the second audio unit
280*d9f75844SAndroid Build Coastguard Worker  // has been done successfully and that there is no conflict with the already
281*d9f75844SAndroid Build Coastguard Worker  // playing first ADM.
282*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
283*d9f75844SAndroid Build Coastguard Worker  __block int num_callbacks = 0;
284*d9f75844SAndroid Build Coastguard Worker
285*d9f75844SAndroid Build Coastguard Worker  MockAudioTransport mock2;
286*d9f75844SAndroid Build Coastguard Worker  mock2.expectNeedMorePlayData(^int32_t(const size_t nSamples,
287*d9f75844SAndroid Build Coastguard Worker                                        const size_t nBytesPerSample,
288*d9f75844SAndroid Build Coastguard Worker                                        const size_t nChannels,
289*d9f75844SAndroid Build Coastguard Worker                                        const uint32_t samplesPerSec,
290*d9f75844SAndroid Build Coastguard Worker                                        void *audioSamples,
291*d9f75844SAndroid Build Coastguard Worker                                        size_t &nSamplesOut,
292*d9f75844SAndroid Build Coastguard Worker                                        int64_t *elapsed_time_ms,
293*d9f75844SAndroid Build Coastguard Worker                                        int64_t *ntp_time_ms) {
294*d9f75844SAndroid Build Coastguard Worker    nSamplesOut = nSamples;
295*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer());
296*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nBytesPerSample, kBytesPerSample);
297*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nChannels, self.playoutParameters.channels());
298*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate());
299*d9f75844SAndroid Build Coastguard Worker    XCTAssertNotEqual((void*)NULL, audioSamples);
300*d9f75844SAndroid Build Coastguard Worker    if (++num_callbacks == kNumCallbacks) {
301*d9f75844SAndroid Build Coastguard Worker      [playoutExpectation fulfill];
302*d9f75844SAndroid Build Coastguard Worker    }
303*d9f75844SAndroid Build Coastguard Worker
304*d9f75844SAndroid Build Coastguard Worker    return 0;
305*d9f75844SAndroid Build Coastguard Worker  });
306*d9f75844SAndroid Build Coastguard Worker
307*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, secondAudioDeviceModule->RegisterAudioCallback(&mock2));
308*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, secondAudioDeviceModule->StartPlayout());
309*d9f75844SAndroid Build Coastguard Worker  XCTAssertTrue(secondAudioDeviceModule->Playing());
310*d9f75844SAndroid Build Coastguard Worker  [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
311*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
312*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, secondAudioDeviceModule->StopPlayout());
313*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(secondAudioDeviceModule->Playing());
314*d9f75844SAndroid Build Coastguard Worker  XCTAssertFalse(secondAudioDeviceModule->PlayoutIsInitialized());
315*d9f75844SAndroid Build Coastguard Worker
316*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, secondAudioDeviceModule->Terminate());
317*d9f75844SAndroid Build Coastguard Worker}
318*d9f75844SAndroid Build Coastguard Worker
319*d9f75844SAndroid Build Coastguard Worker// Start playout and verify that the native audio layer starts asking for real
320*d9f75844SAndroid Build Coastguard Worker// audio samples to play out using the NeedMorePlayData callback.
321*d9f75844SAndroid Build Coastguard Worker- (void)testStartPlayoutVerifyCallbacks {
322*d9f75844SAndroid Build Coastguard Worker
323*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
324*d9f75844SAndroid Build Coastguard Worker  __block int num_callbacks = 0;
325*d9f75844SAndroid Build Coastguard Worker  mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
326*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
327*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
328*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
329*d9f75844SAndroid Build Coastguard Worker                                       void *audioSamples,
330*d9f75844SAndroid Build Coastguard Worker                                       size_t &nSamplesOut,
331*d9f75844SAndroid Build Coastguard Worker                                       int64_t *elapsed_time_ms,
332*d9f75844SAndroid Build Coastguard Worker                                       int64_t *ntp_time_ms) {
333*d9f75844SAndroid Build Coastguard Worker    nSamplesOut = nSamples;
334*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer());
335*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nBytesPerSample, kBytesPerSample);
336*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nChannels, self.playoutParameters.channels());
337*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate());
338*d9f75844SAndroid Build Coastguard Worker    XCTAssertNotEqual((void*)NULL, audioSamples);
339*d9f75844SAndroid Build Coastguard Worker    if (++num_callbacks == kNumCallbacks) {
340*d9f75844SAndroid Build Coastguard Worker      [playoutExpectation fulfill];
341*d9f75844SAndroid Build Coastguard Worker    }
342*d9f75844SAndroid Build Coastguard Worker    return 0;
343*d9f75844SAndroid Build Coastguard Worker  });
344*d9f75844SAndroid Build Coastguard Worker
345*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
346*d9f75844SAndroid Build Coastguard Worker
347*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
348*d9f75844SAndroid Build Coastguard Worker  [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
349*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
350*d9f75844SAndroid Build Coastguard Worker}
351*d9f75844SAndroid Build Coastguard Worker
352*d9f75844SAndroid Build Coastguard Worker// Start recording and verify that the native audio layer starts feeding real
353*d9f75844SAndroid Build Coastguard Worker// audio samples via the RecordedDataIsAvailable callback.
354*d9f75844SAndroid Build Coastguard Worker- (void)testStartRecordingVerifyCallbacks {
355*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *recordExpectation =
356*d9f75844SAndroid Build Coastguard Worker  [self expectationWithDescription:@"RecordedDataIsAvailable"];
357*d9f75844SAndroid Build Coastguard Worker  __block int num_callbacks = 0;
358*d9f75844SAndroid Build Coastguard Worker
359*d9f75844SAndroid Build Coastguard Worker  mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
360*d9f75844SAndroid Build Coastguard Worker                                       const size_t nSamples,
361*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
362*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
363*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
364*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t totalDelayMS,
365*d9f75844SAndroid Build Coastguard Worker                                       const int32_t clockDrift,
366*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t currentMicLevel,
367*d9f75844SAndroid Build Coastguard Worker                                       const bool keyPressed,
368*d9f75844SAndroid Build Coastguard Worker                                       uint32_t& newMicLevel) {
369*d9f75844SAndroid Build Coastguard Worker    XCTAssertNotEqual((void*)NULL, audioSamples);
370*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nSamples, self.recordParameters.frames_per_10ms_buffer());
371*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nBytesPerSample, kBytesPerSample);
372*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nChannels, self.recordParameters.channels());
373*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual((int)samplesPerSec, self.recordParameters.sample_rate());
374*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(0, clockDrift);
375*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(0u, currentMicLevel);
376*d9f75844SAndroid Build Coastguard Worker    XCTAssertFalse(keyPressed);
377*d9f75844SAndroid Build Coastguard Worker    if (++num_callbacks == kNumCallbacks) {
378*d9f75844SAndroid Build Coastguard Worker      [recordExpectation fulfill];
379*d9f75844SAndroid Build Coastguard Worker    }
380*d9f75844SAndroid Build Coastguard Worker
381*d9f75844SAndroid Build Coastguard Worker    return 0;
382*d9f75844SAndroid Build Coastguard Worker  });
383*d9f75844SAndroid Build Coastguard Worker
384*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
385*d9f75844SAndroid Build Coastguard Worker  [self startRecording];
386*d9f75844SAndroid Build Coastguard Worker  [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
387*d9f75844SAndroid Build Coastguard Worker  [self stopRecording];
388*d9f75844SAndroid Build Coastguard Worker}
389*d9f75844SAndroid Build Coastguard Worker
390*d9f75844SAndroid Build Coastguard Worker// Start playout and recording (full-duplex audio) and verify that audio is
391*d9f75844SAndroid Build Coastguard Worker// active in both directions.
392*d9f75844SAndroid Build Coastguard Worker- (void)testStartPlayoutAndRecordingVerifyCallbacks {
393*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
394*d9f75844SAndroid Build Coastguard Worker  __block NSUInteger callbackCount = 0;
395*d9f75844SAndroid Build Coastguard Worker
396*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *recordExpectation =
397*d9f75844SAndroid Build Coastguard Worker  [self expectationWithDescription:@"RecordedDataIsAvailable"];
398*d9f75844SAndroid Build Coastguard Worker  recordExpectation.expectedFulfillmentCount = kNumCallbacks;
399*d9f75844SAndroid Build Coastguard Worker
400*d9f75844SAndroid Build Coastguard Worker  mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
401*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
402*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
403*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
404*d9f75844SAndroid Build Coastguard Worker                                       void *audioSamples,
405*d9f75844SAndroid Build Coastguard Worker                                       size_t &nSamplesOut,
406*d9f75844SAndroid Build Coastguard Worker                                       int64_t *elapsed_time_ms,
407*d9f75844SAndroid Build Coastguard Worker                                       int64_t *ntp_time_ms) {
408*d9f75844SAndroid Build Coastguard Worker    nSamplesOut = nSamples;
409*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nSamples, self.playoutParameters.frames_per_10ms_buffer());
410*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nBytesPerSample, kBytesPerSample);
411*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nChannels, self.playoutParameters.channels());
412*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual((int)samplesPerSec, self.playoutParameters.sample_rate());
413*d9f75844SAndroid Build Coastguard Worker    XCTAssertNotEqual((void*)NULL, audioSamples);
414*d9f75844SAndroid Build Coastguard Worker    if (callbackCount++ >= kNumCallbacks) {
415*d9f75844SAndroid Build Coastguard Worker      [playoutExpectation fulfill];
416*d9f75844SAndroid Build Coastguard Worker    }
417*d9f75844SAndroid Build Coastguard Worker
418*d9f75844SAndroid Build Coastguard Worker    return 0;
419*d9f75844SAndroid Build Coastguard Worker  });
420*d9f75844SAndroid Build Coastguard Worker
421*d9f75844SAndroid Build Coastguard Worker  mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
422*d9f75844SAndroid Build Coastguard Worker                                       const size_t nSamples,
423*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
424*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
425*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
426*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t totalDelayMS,
427*d9f75844SAndroid Build Coastguard Worker                                       const int32_t clockDrift,
428*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t currentMicLevel,
429*d9f75844SAndroid Build Coastguard Worker                                       const bool keyPressed,
430*d9f75844SAndroid Build Coastguard Worker                                       uint32_t& newMicLevel) {
431*d9f75844SAndroid Build Coastguard Worker    XCTAssertNotEqual((void*)NULL, audioSamples);
432*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nSamples, self.recordParameters.frames_per_10ms_buffer());
433*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nBytesPerSample, kBytesPerSample);
434*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(nChannels, self.recordParameters.channels());
435*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual((int)samplesPerSec, self.recordParameters.sample_rate());
436*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(0, clockDrift);
437*d9f75844SAndroid Build Coastguard Worker    XCTAssertEqual(0u, currentMicLevel);
438*d9f75844SAndroid Build Coastguard Worker    XCTAssertFalse(keyPressed);
439*d9f75844SAndroid Build Coastguard Worker    [recordExpectation fulfill];
440*d9f75844SAndroid Build Coastguard Worker
441*d9f75844SAndroid Build Coastguard Worker    return 0;
442*d9f75844SAndroid Build Coastguard Worker  });
443*d9f75844SAndroid Build Coastguard Worker
444*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
445*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
446*d9f75844SAndroid Build Coastguard Worker  [self startRecording];
447*d9f75844SAndroid Build Coastguard Worker  [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
448*d9f75844SAndroid Build Coastguard Worker  [self stopRecording];
449*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
450*d9f75844SAndroid Build Coastguard Worker}
451*d9f75844SAndroid Build Coastguard Worker
452*d9f75844SAndroid Build Coastguard Worker// Start playout and read audio from an external PCM file when the audio layer
453*d9f75844SAndroid Build Coastguard Worker// asks for data to play out. Real audio is played out in this test but it does
454*d9f75844SAndroid Build Coastguard Worker// not contain any explicit verification that the audio quality is perfect.
455*d9f75844SAndroid Build Coastguard Worker- (void)testRunPlayoutWithFileAsSource {
456*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(1u, playoutParameters.channels());
457*d9f75844SAndroid Build Coastguard Worker
458*d9f75844SAndroid Build Coastguard Worker  // Using XCTestExpectation to count callbacks is very slow.
459*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
460*d9f75844SAndroid Build Coastguard Worker  const int expectedCallbackCount = kFilePlayTimeInSec * kNumCallbacksPerSecond;
461*d9f75844SAndroid Build Coastguard Worker  __block int callbackCount = 0;
462*d9f75844SAndroid Build Coastguard Worker
463*d9f75844SAndroid Build Coastguard Worker  NSURL *fileURL = [self fileURLForSampleRate:playoutParameters.sample_rate()];
464*d9f75844SAndroid Build Coastguard Worker  NSInputStream *inputStream = [[NSInputStream alloc] initWithURL:fileURL];
465*d9f75844SAndroid Build Coastguard Worker
466*d9f75844SAndroid Build Coastguard Worker  mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
467*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
468*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
469*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
470*d9f75844SAndroid Build Coastguard Worker                                       void *audioSamples,
471*d9f75844SAndroid Build Coastguard Worker                                       size_t &nSamplesOut,
472*d9f75844SAndroid Build Coastguard Worker                                       int64_t *elapsed_time_ms,
473*d9f75844SAndroid Build Coastguard Worker                                       int64_t *ntp_time_ms) {
474*d9f75844SAndroid Build Coastguard Worker    [inputStream read:(uint8_t *)audioSamples maxLength:nSamples*nBytesPerSample*nChannels];
475*d9f75844SAndroid Build Coastguard Worker    nSamplesOut = nSamples;
476*d9f75844SAndroid Build Coastguard Worker    if (callbackCount++ == expectedCallbackCount) {
477*d9f75844SAndroid Build Coastguard Worker      [playoutExpectation fulfill];
478*d9f75844SAndroid Build Coastguard Worker    }
479*d9f75844SAndroid Build Coastguard Worker
480*d9f75844SAndroid Build Coastguard Worker    return 0;
481*d9f75844SAndroid Build Coastguard Worker  });
482*d9f75844SAndroid Build Coastguard Worker
483*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
484*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
485*d9f75844SAndroid Build Coastguard Worker  NSTimeInterval waitTimeout = kFilePlayTimeInSec * 2.0;
486*d9f75844SAndroid Build Coastguard Worker  [self waitForExpectationsWithTimeout:waitTimeout handler:nil];
487*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
488*d9f75844SAndroid Build Coastguard Worker}
489*d9f75844SAndroid Build Coastguard Worker
490*d9f75844SAndroid Build Coastguard Worker- (void)testDevices {
491*d9f75844SAndroid Build Coastguard Worker  // Device enumeration is not supported. Verify fixed values only.
492*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(1, audioDeviceModule->PlayoutDevices());
493*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(1, audioDeviceModule->RecordingDevices());
494*d9f75844SAndroid Build Coastguard Worker}
495*d9f75844SAndroid Build Coastguard Worker
496*d9f75844SAndroid Build Coastguard Worker// Start playout and recording and store recorded data in an intermediate FIFO
497*d9f75844SAndroid Build Coastguard Worker// buffer from which the playout side then reads its samples in the same order
498*d9f75844SAndroid Build Coastguard Worker// as they were stored. Under ideal circumstances, a callback sequence would
499*d9f75844SAndroid Build Coastguard Worker// look like: ...+-+-+-+-+-+-+-..., where '+' means 'packet recorded' and '-'
500*d9f75844SAndroid Build Coastguard Worker// means 'packet played'. Under such conditions, the FIFO would only contain
501*d9f75844SAndroid Build Coastguard Worker// one packet on average. However, under more realistic conditions, the size
502*d9f75844SAndroid Build Coastguard Worker// of the FIFO will vary more due to an unbalance between the two sides.
503*d9f75844SAndroid Build Coastguard Worker// This test tries to verify that the device maintains a balanced callback-
504*d9f75844SAndroid Build Coastguard Worker// sequence by running in loopback for ten seconds while measuring the size
505*d9f75844SAndroid Build Coastguard Worker// (max and average) of the FIFO. The size of the FIFO is increased by the
506*d9f75844SAndroid Build Coastguard Worker// recording side and decreased by the playout side.
507*d9f75844SAndroid Build Coastguard Worker// TODO(henrika): tune the final test parameters after running tests on several
508*d9f75844SAndroid Build Coastguard Worker// different devices.
509*d9f75844SAndroid Build Coastguard Worker- (void)testRunPlayoutAndRecordingInFullDuplex {
510*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(recordParameters.channels(), playoutParameters.channels());
511*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(recordParameters.sample_rate(), playoutParameters.sample_rate());
512*d9f75844SAndroid Build Coastguard Worker
513*d9f75844SAndroid Build Coastguard Worker  XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
514*d9f75844SAndroid Build Coastguard Worker  __block NSUInteger playoutCallbacks = 0;
515*d9f75844SAndroid Build Coastguard Worker  NSUInteger expectedPlayoutCallbacks = kFullDuplexTimeInSec * kNumCallbacksPerSecond;
516*d9f75844SAndroid Build Coastguard Worker
517*d9f75844SAndroid Build Coastguard Worker  // FIFO queue and measurements
518*d9f75844SAndroid Build Coastguard Worker  NSMutableArray *fifoBuffer = [NSMutableArray arrayWithCapacity:20];
519*d9f75844SAndroid Build Coastguard Worker  __block NSUInteger fifoMaxSize = 0;
520*d9f75844SAndroid Build Coastguard Worker  __block NSUInteger fifoTotalWrittenElements = 0;
521*d9f75844SAndroid Build Coastguard Worker  __block NSUInteger fifoWriteCount = 0;
522*d9f75844SAndroid Build Coastguard Worker
523*d9f75844SAndroid Build Coastguard Worker  mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
524*d9f75844SAndroid Build Coastguard Worker                                       const size_t nSamples,
525*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
526*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
527*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
528*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t totalDelayMS,
529*d9f75844SAndroid Build Coastguard Worker                                       const int32_t clockDrift,
530*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t currentMicLevel,
531*d9f75844SAndroid Build Coastguard Worker                                       const bool keyPressed,
532*d9f75844SAndroid Build Coastguard Worker                                       uint32_t& newMicLevel) {
533*d9f75844SAndroid Build Coastguard Worker    if (fifoWriteCount++ < kNumIgnoreFirstCallbacks) {
534*d9f75844SAndroid Build Coastguard Worker      return 0;
535*d9f75844SAndroid Build Coastguard Worker    }
536*d9f75844SAndroid Build Coastguard Worker
537*d9f75844SAndroid Build Coastguard Worker    NSData *data = [NSData dataWithBytes:audioSamples length:nSamples*nBytesPerSample*nChannels];
538*d9f75844SAndroid Build Coastguard Worker    @synchronized(fifoBuffer) {
539*d9f75844SAndroid Build Coastguard Worker      [fifoBuffer addObject:data];
540*d9f75844SAndroid Build Coastguard Worker      fifoMaxSize = MAX(fifoMaxSize, fifoBuffer.count);
541*d9f75844SAndroid Build Coastguard Worker      fifoTotalWrittenElements += fifoBuffer.count;
542*d9f75844SAndroid Build Coastguard Worker    }
543*d9f75844SAndroid Build Coastguard Worker
544*d9f75844SAndroid Build Coastguard Worker    return 0;
545*d9f75844SAndroid Build Coastguard Worker  });
546*d9f75844SAndroid Build Coastguard Worker
547*d9f75844SAndroid Build Coastguard Worker  mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
548*d9f75844SAndroid Build Coastguard Worker                                       const size_t nBytesPerSample,
549*d9f75844SAndroid Build Coastguard Worker                                       const size_t nChannels,
550*d9f75844SAndroid Build Coastguard Worker                                       const uint32_t samplesPerSec,
551*d9f75844SAndroid Build Coastguard Worker                                       void *audioSamples,
552*d9f75844SAndroid Build Coastguard Worker                                       size_t &nSamplesOut,
553*d9f75844SAndroid Build Coastguard Worker                                       int64_t *elapsed_time_ms,
554*d9f75844SAndroid Build Coastguard Worker                                       int64_t *ntp_time_ms) {
555*d9f75844SAndroid Build Coastguard Worker    nSamplesOut = nSamples;
556*d9f75844SAndroid Build Coastguard Worker    NSData *data;
557*d9f75844SAndroid Build Coastguard Worker    @synchronized(fifoBuffer) {
558*d9f75844SAndroid Build Coastguard Worker      data = fifoBuffer.firstObject;
559*d9f75844SAndroid Build Coastguard Worker      if (data) {
560*d9f75844SAndroid Build Coastguard Worker        [fifoBuffer removeObjectAtIndex:0];
561*d9f75844SAndroid Build Coastguard Worker      }
562*d9f75844SAndroid Build Coastguard Worker    }
563*d9f75844SAndroid Build Coastguard Worker
564*d9f75844SAndroid Build Coastguard Worker    if (data) {
565*d9f75844SAndroid Build Coastguard Worker      memcpy(audioSamples, (char*) data.bytes, data.length);
566*d9f75844SAndroid Build Coastguard Worker    } else {
567*d9f75844SAndroid Build Coastguard Worker      memset(audioSamples, 0, nSamples*nBytesPerSample*nChannels);
568*d9f75844SAndroid Build Coastguard Worker    }
569*d9f75844SAndroid Build Coastguard Worker
570*d9f75844SAndroid Build Coastguard Worker    if (playoutCallbacks++ == expectedPlayoutCallbacks) {
571*d9f75844SAndroid Build Coastguard Worker      [playoutExpectation fulfill];
572*d9f75844SAndroid Build Coastguard Worker    }
573*d9f75844SAndroid Build Coastguard Worker    return 0;
574*d9f75844SAndroid Build Coastguard Worker  });
575*d9f75844SAndroid Build Coastguard Worker
576*d9f75844SAndroid Build Coastguard Worker  XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
577*d9f75844SAndroid Build Coastguard Worker  [self startRecording];
578*d9f75844SAndroid Build Coastguard Worker  [self startPlayout];
579*d9f75844SAndroid Build Coastguard Worker  NSTimeInterval waitTimeout = kFullDuplexTimeInSec * 2.0;
580*d9f75844SAndroid Build Coastguard Worker  [self waitForExpectationsWithTimeout:waitTimeout handler:nil];
581*d9f75844SAndroid Build Coastguard Worker
582*d9f75844SAndroid Build Coastguard Worker  size_t fifoAverageSize =
583*d9f75844SAndroid Build Coastguard Worker      (fifoTotalWrittenElements == 0)
584*d9f75844SAndroid Build Coastguard Worker        ? 0.0
585*d9f75844SAndroid Build Coastguard Worker        : 0.5 + (double)fifoTotalWrittenElements / (fifoWriteCount - kNumIgnoreFirstCallbacks);
586*d9f75844SAndroid Build Coastguard Worker
587*d9f75844SAndroid Build Coastguard Worker  [self stopPlayout];
588*d9f75844SAndroid Build Coastguard Worker  [self stopRecording];
589*d9f75844SAndroid Build Coastguard Worker  XCTAssertLessThan(fifoAverageSize, 10u);
590*d9f75844SAndroid Build Coastguard Worker  XCTAssertLessThan(fifoMaxSize, 20u);
591*d9f75844SAndroid Build Coastguard Worker}
592*d9f75844SAndroid Build Coastguard Worker
593*d9f75844SAndroid Build Coastguard Worker@end
594