xref: /aosp_15_r20/cts/apps/CtsVerifier/src/org/hyphonate/megaaudio/duplex/DuplexAudioManager.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright 2020 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 package org.hyphonate.megaaudio.duplex;
17 
18 import android.media.AudioDeviceInfo;
19 import android.util.Log;
20 
21 import org.hyphonate.megaaudio.common.BuilderBase;
22 import org.hyphonate.megaaudio.common.StreamBase;
23 import org.hyphonate.megaaudio.player.AudioSource;
24 import org.hyphonate.megaaudio.player.AudioSourceProvider;
25 import org.hyphonate.megaaudio.player.Player;
26 import org.hyphonate.megaaudio.player.PlayerBuilder;
27 import org.hyphonate.megaaudio.recorder.AudioSinkProvider;
28 import org.hyphonate.megaaudio.recorder.Recorder;
29 import org.hyphonate.megaaudio.recorder.RecorderBuilder;
30 
31 public class DuplexAudioManager {
32     @SuppressWarnings("unused")
33     private static final String TAG = DuplexAudioManager.class.getSimpleName();
34     @SuppressWarnings("unused")
35     private static final boolean LOG = true;
36 
37     // Player
38     //TODO - explain these constants
39     private int mNumPlayerChannels = 2;
40     private int mPlayerChannelMask = 0;
41 
42     private int mPlayerSampleRate = 48000;
43     private int mNumPlayerBurstFrames;
44 
45     // see Performance Mode Constants in BuilderBase.java
46     private int mPlayerPerformanceMode = BuilderBase.PERFORMANCE_MODE_LOWLATENCY;
47     private int mRecorderPerformanceMode = BuilderBase.PERFORMANCE_MODE_LOWLATENCY;
48 
49     private Player mPlayer;
50     private AudioSourceProvider mSourceProvider;
51     private AudioDeviceInfo mPlayerSelectedDevice;
52 
53     // Recorder
54     private int mNumRecorderChannels = 2;
55     private int mRecorderSampleRate = 48000;
56     private int mNumRecorderBufferFrames;
57 
58     private Recorder mRecorder;
59     private AudioSinkProvider mSinkProvider;
60     private AudioDeviceInfo mRecorderSelectedDevice;
61     private int mInputPreset = Recorder.INPUT_PRESET_NONE;
62 
63     private int mPlayerSharingMode = BuilderBase.SHARING_MODE_SHARED;
64     private int mRecorderSharingMode = BuilderBase.SHARING_MODE_SHARED;
65 
DuplexAudioManager(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider)66     public DuplexAudioManager(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider) {
67         setSources(sourceProvider, sinkProvider);
68     }
69 
70     /**
71      * Specify the source providers for the source and sink.
72      * @param sourceProvider The AudioSourceProvider for the output stream
73      * @param sinkProvider The AudioSinkProvider for the input stream.
74      */
setSources(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider)75     public void setSources(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider) {
76         mSourceProvider = sourceProvider;
77         mSinkProvider = sinkProvider;
78 
79         mPlayerSampleRate =  StreamBase.getSystemSampleRate();
80         mRecorderSampleRate = StreamBase.getSystemSampleRate();
81     }
82 
83     //
84     // Be careful using these, they will change after setupStreams is called.
85     //
getPlayer()86     public Player getPlayer() {
87         return mPlayer;
88     }
getRecorder()89     public Recorder getRecorder() {
90         return mRecorder;
91     }
92 
setPlayerSampleRate(int sampleRate)93     public void setPlayerSampleRate(int sampleRate) {
94         mPlayerSampleRate = sampleRate;
95     }
96 
setRecordererSampleRate(int sampleRate)97     public void setRecordererSampleRate(int sampleRate) {
98         mPlayerSampleRate = sampleRate;
99     }
100 
setPlayerRouteDevice(AudioDeviceInfo deviceInfo)101     public void setPlayerRouteDevice(AudioDeviceInfo deviceInfo) {
102         mPlayerSelectedDevice = deviceInfo;
103     }
104 
setRecorderRouteDevice(AudioDeviceInfo deviceInfo)105     public void setRecorderRouteDevice(AudioDeviceInfo deviceInfo) {
106         mRecorderSelectedDevice = deviceInfo;
107     }
108 
109     /**
110      * Specifies the number of player (index) channels.
111      * @param numChannels The number of index channels for the player.
112      */
setNumPlayerChannels(int numChannels)113     public void setNumPlayerChannels(int numChannels) {
114         mNumPlayerChannels = numChannels;
115         mPlayerChannelMask = 0;
116     }
117 
118     /**
119      * Specifies the positional-mask for the player.
120      * @param mask - An AudioFormat position mask.
121      */
setPlayerChannelMask(int mask)122     public void setPlayerChannelMask(int mask) {
123         mPlayerChannelMask = mask;
124         mNumPlayerChannels = 0;
125     }
126 
setNumRecorderChannels(int numChannels)127     public void setNumRecorderChannels(int numChannels) {
128         mNumRecorderChannels = numChannels;
129     }
setRecorderSampleRate(int sampleRate)130     public void setRecorderSampleRate(int sampleRate) {
131         mRecorderSampleRate = sampleRate;
132     }
133 
setPlayerSharingMode(int mode)134     public void setPlayerSharingMode(int mode) {
135         mPlayerSharingMode = mode;
136     }
137 
setRecorderSharingMode(int mode)138     public void setRecorderSharingMode(int mode) {
139         mRecorderSharingMode = mode;
140     }
141 
getPlayerChannelCount()142     public int getPlayerChannelCount() {
143         return mPlayer != null ? mPlayer.getChannelCount() : -1;
144     }
145 
getRecorderChannelCount()146     public int getRecorderChannelCount() {
147         return mRecorder != null ? mRecorder.getChannelCount() : -1;
148     }
149 
150     /**
151      * Specifies the Performance Mode.
152      */
setPlayerPerformanceMode(int performanceMode)153     public void setPlayerPerformanceMode(int performanceMode) {
154         mPlayerPerformanceMode = performanceMode;
155     }
156 
getPlayerPerformanceMode()157     public int getPlayerPerformanceMode() {
158         return mPlayerPerformanceMode;
159     }
160 
161     /**
162      * Specifies the Performance Mode.
163      */
setRecorderPerformanceMode(int performanceMode)164     public void setRecorderPerformanceMode(int performanceMode) {
165         mRecorderPerformanceMode = performanceMode;
166     }
167 
getRecorderPerformanceMode()168     public int getRecorderPerformanceMode() {
169         return mRecorderPerformanceMode;
170     }
171 
172     /**
173      * Specifies the input preset to use for the recorder.
174      * @param preset
175      */
setInputPreset(int preset)176     public void setInputPreset(int preset) {
177         mInputPreset = preset;
178     }
179 
180     /**
181      * Initializes (but does not start) the player and recorder streams.
182      * @param playerType    The API constant for the player
183      * @param recorderType  The API constant for the recorder
184      * @return a StreamBase status code specifying the result.
185      */
buildStreams(int playerType, int recorderType)186     public int buildStreams(int playerType, int recorderType) {
187         // Recorder
188         if ((recorderType & BuilderBase.TYPE_MASK) != BuilderBase.TYPE_NONE) {
189             try {
190                 mNumRecorderBufferFrames = StreamBase.getNumBurstFrames(BuilderBase.TYPE_NONE);
191                 RecorderBuilder builder = (RecorderBuilder) new RecorderBuilder()
192                         .setRecorderType(recorderType)
193                         .setAudioSinkProvider(mSinkProvider)
194                         .setInputPreset(mInputPreset)
195                         .setSharingMode(mRecorderSharingMode)
196                         .setRouteDevice(mRecorderSelectedDevice)
197                         .setSampleRate(mRecorderSampleRate)
198                         .setChannelCount(mNumRecorderChannels)
199                         .setNumExchangeFrames(mNumRecorderBufferFrames)
200                         .setPerformanceMode(mRecorderPerformanceMode);
201                 mRecorder = builder.build();
202             } catch (RecorderBuilder.BadStateException ex) {
203                 Log.e(TAG, "Recorder - BadStateException" + ex);
204                 return StreamBase.ERROR_UNSUPPORTED;
205             }
206         }
207 
208         // Player
209         if ((playerType & BuilderBase.TYPE_MASK) != BuilderBase.TYPE_NONE) {
210             try {
211                 mNumPlayerBurstFrames = StreamBase.getNumBurstFrames(playerType);
212                 PlayerBuilder builder = (PlayerBuilder) new PlayerBuilder()
213                         .setPlayerType(playerType)
214                         .setSourceProvider(mSourceProvider)
215                         .setSampleRate(mPlayerSampleRate)
216                         .setChannelCount(mNumPlayerChannels)
217                         .setSharingMode(mPlayerSharingMode)
218                         .setRouteDevice(mPlayerSelectedDevice)
219                         .setNumExchangeFrames(mNumPlayerBurstFrames)
220                         .setPerformanceMode(mPlayerPerformanceMode);
221                 if (mNumPlayerChannels == 0) {
222                     builder.setChannelMask(mPlayerChannelMask);
223                 } else {
224                     builder.setChannelCount(mNumPlayerChannels);
225                 }
226                 mPlayer = builder.build();
227             } catch (PlayerBuilder.BadStateException ex) {
228                 Log.e(TAG, "Player - BadStateException" + ex);
229                 return StreamBase.ERROR_UNSUPPORTED;
230             } catch (Exception ex) {
231                 Log.e(TAG, "Uncaught Error in Player Setup for DuplexAudioManager ex:" + ex);
232             }
233         }
234 
235         return StreamBase.OK;
236     }
237 
start()238     public int start() {
239         if (LOG) {
240             Log.d(TAG, "start()...");
241         }
242 
243         int result = StreamBase.OK;
244         if (mPlayer != null && (result = mPlayer.startStream()) != StreamBase.OK) {
245             if (LOG) {
246                 Log.d(TAG, "  player fails result:" + result);
247             }
248             return result;
249         }
250 
251         if (mRecorder != null && (result = mRecorder.startStream()) != StreamBase.OK) {
252             if (LOG) {
253                 Log.d(TAG, "  recorder fails result:" + result);
254             }
255             // Shut down
256             stop();
257 
258             return result;
259         }
260 
261         if (LOG) {
262             Log.d(TAG, "  result:" + result);
263         }
264         return result;
265     }
266 
stop()267     public int stop() {
268         if (LOG) {
269             Log.d(TAG, "stop()");
270         }
271         int playerResult = StreamBase.OK;
272         if (mPlayer != null) {
273             int result1 = mPlayer.stopStream();
274             int result2 = mPlayer.teardownStream();
275             playerResult = result1 != StreamBase.OK ? result1 : result2;
276         }
277 
278         int recorderResult = StreamBase.OK;
279         if (mRecorder != null) {
280             int result1 = mRecorder.stopStream();
281             int result2 = mRecorder.teardownStream();
282             recorderResult = result1 != StreamBase.OK ? result1 : result2;
283         }
284 
285         int ret = playerResult != StreamBase.OK ? playerResult : recorderResult;
286 
287         if (LOG) {
288             Log.d(TAG, "  returns:" + ret);
289         }
290         return ret;
291     }
292 
getNumPlayerBufferFrames()293     public int getNumPlayerBufferFrames() {
294         return mPlayer != null ? mPlayer.getSystemBurstFrames() : 0;
295     }
296 
getNumRecorderBufferFrames()297     public int getNumRecorderBufferFrames() {
298         return mRecorder != null ? mRecorder.getSystemBurstFrames() : 0;
299     }
300 
getAudioSource()301     public AudioSource getAudioSource() {
302         return mPlayer != null ? mPlayer.getAudioSource() : null;
303     }
304 
305     /**
306      * Don't call this until the streams are started
307      * @return true if both player and recorder are routed to the devices specified
308      * with setRecorderRouteDevice() and setPlayerRouteDevice().
309      */
validateRouting()310     public boolean validateRouting() {
311         if (mPlayerSelectedDevice == null && mRecorderSelectedDevice == null) {
312             return true;
313         }
314 
315         if (mPlayer == null || !mPlayer.isPlaying()
316                 || mRecorder == null || !mRecorder.isRecording()) {
317             return false;
318         }
319 
320         if (mPlayerSelectedDevice != null
321                 && mPlayer.getRoutedDeviceId() != mPlayerSelectedDevice.getId()) {
322             return false;
323         }
324 
325         if (mRecorderSelectedDevice != null
326                 && mRecorder.getRoutedDeviceId() != mRecorderSelectedDevice.getId()) {
327             return false;
328         }
329 
330         // Everything checks out OK.
331         return true;
332     }
333 
334     /**
335      * Don't call this until the streams are started
336      * @return true if the player is using the specified sharing mode set with
337      * setPlayerSharingMode().
338      */
isSpecifiedPlayerSharingMode()339     public boolean isSpecifiedPlayerSharingMode() {
340         boolean playerOK = false;
341         if (mPlayer != null) {
342             int sharingMode = mPlayer.getSharingMode();
343             playerOK = sharingMode == mPlayerSharingMode
344                     || sharingMode == BuilderBase.SHARING_MODE_NOTSUPPORTED;
345         }
346         return playerOK;
347     }
348 
349     /**
350      * Don't call this until the streams are started
351      * @return true if the recorder is using the specified sharing mode set with
352      * setRecorderSharingMode().
353      */
isSpecifiedRecorderSharingMode()354     public boolean isSpecifiedRecorderSharingMode() {
355         boolean recorderOK = false;
356         if (mRecorder != null) {
357             int sharingMode = mRecorder.getSharingMode();
358             recorderOK = sharingMode == mRecorderSharingMode
359                     || sharingMode == BuilderBase.SHARING_MODE_NOTSUPPORTED;
360         }
361         return recorderOK;
362     }
363 
364     /**
365      * Don't call this until the streams are started
366      * @return true if the player is using MMAP.
367      */
isPlayerStreamMMap()368     public boolean isPlayerStreamMMap() {
369         return mPlayer.isMMap();
370     }
371 
372     /**
373      * Don't call this until the streams are started
374      * @return true if the recorders is using MMAP.
375      */
isRecorderStreamMMap()376     public boolean isRecorderStreamMMap() {
377         return mRecorder.isMMap();
378     }
379 }
380