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