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 mPlayerSampleRate = 48000; 41 private int mNumPlayerBurstFrames; 42 43 private Player mPlayer; 44 private AudioSourceProvider mSourceProvider; 45 private AudioDeviceInfo mPlayerSelectedDevice; 46 47 // Recorder 48 private int mNumRecorderChannels = 2; 49 private int mRecorderSampleRate = 48000; 50 private int mNumRecorderBufferFrames; 51 52 private Recorder mRecorder; 53 private AudioSinkProvider mSinkProvider; 54 private AudioDeviceInfo mRecorderSelectedDevice; 55 private int mInputPreset = Recorder.INPUT_PRESET_NONE; 56 DuplexAudioManager(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider)57 public DuplexAudioManager(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider) { 58 setSources(sourceProvider, sinkProvider); 59 } 60 61 /** 62 * Specify the source providers for the source and sink. 63 * @param sourceProvider The AudioSourceProvider for the output stream 64 * @param sinkProvider The AudioSinkProvider for the input stream. 65 */ setSources(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider)66 public void setSources(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider) { 67 mSourceProvider = sourceProvider; 68 mSinkProvider = sinkProvider; 69 70 mPlayerSampleRate = StreamBase.getSystemSampleRate(); 71 mRecorderSampleRate = StreamBase.getSystemSampleRate(); 72 } 73 74 // 75 // Be careful using these, they will change after setupStreams is called. 76 // getPlayer()77 public Player getPlayer() { 78 return mPlayer; 79 } getRecorder()80 public Recorder getRecorder() { 81 return mRecorder; 82 } 83 setPlayerSampleRate(int sampleRate)84 public void setPlayerSampleRate(int sampleRate) { 85 mPlayerSampleRate = sampleRate; 86 } 87 setRecordererSampleRate(int sampleRate)88 public void setRecordererSampleRate(int sampleRate) { 89 mPlayerSampleRate = sampleRate; 90 } 91 setPlayerRouteDevice(AudioDeviceInfo deviceInfo)92 public void setPlayerRouteDevice(AudioDeviceInfo deviceInfo) { 93 mPlayerSelectedDevice = deviceInfo; 94 } 95 setRecorderRouteDevice(AudioDeviceInfo deviceInfo)96 public void setRecorderRouteDevice(AudioDeviceInfo deviceInfo) { 97 mRecorderSelectedDevice = deviceInfo; 98 } 99 setNumPlayerChannels(int numChannels)100 public void setNumPlayerChannels(int numChannels) { 101 mNumPlayerChannels = numChannels; 102 } 103 setNumRecorderChannels(int numChannels)104 public void setNumRecorderChannels(int numChannels) { 105 mNumRecorderChannels = numChannels; 106 } setRecorderSampleRate(int sampleRate)107 public void setRecorderSampleRate(int sampleRate) { 108 mRecorderSampleRate = sampleRate; 109 } 110 111 /** 112 * Specifies the input preset to use for the recorder. 113 * @param preset 114 */ setInputPreset(int preset)115 public void setInputPreset(int preset) { 116 mInputPreset = preset; 117 } 118 setupStreams(int playerType, int recorderType)119 public int setupStreams(int playerType, int recorderType) { 120 // Recorder 121 if ((recorderType & BuilderBase.TYPE_MASK) != BuilderBase.TYPE_NONE) { 122 try { 123 // mNumRecorderBufferFrames = Recorder.calcMinBufferFramesStatic( 124 // mNumRecorderChannels, mRecorderSampleRate); 125 mNumRecorderBufferFrames = StreamBase.getNumBurstFrames(BuilderBase.TYPE_NONE); 126 RecorderBuilder builder = (RecorderBuilder) new RecorderBuilder() 127 .setRecorderType(recorderType) 128 .setAudioSinkProvider(mSinkProvider) 129 .setInputPreset(mInputPreset) 130 .setRouteDevice(mRecorderSelectedDevice) 131 .setSampleRate(mRecorderSampleRate) 132 .setChannelCount(mNumRecorderChannels) 133 .setNumExchangeFrames(mNumRecorderBufferFrames); 134 mRecorder = builder.build(); 135 } catch (RecorderBuilder.BadStateException ex) { 136 Log.e(TAG, "Recorder - BadStateException" + ex); 137 return StreamBase.ERROR_UNSUPPORTED; 138 } 139 } 140 141 // Player 142 if ((playerType & BuilderBase.TYPE_MASK) != BuilderBase.TYPE_NONE) { 143 try { 144 mNumPlayerBurstFrames = StreamBase.getNumBurstFrames(playerType); 145 PlayerBuilder builder = (PlayerBuilder) new PlayerBuilder() 146 .setPlayerType(playerType) 147 .setSourceProvider(mSourceProvider) 148 .setSampleRate(mPlayerSampleRate) 149 .setChannelCount(mNumPlayerChannels) 150 .setRouteDevice(mPlayerSelectedDevice) 151 .setNumExchangeFrames(mNumPlayerBurstFrames) 152 .setPerformanceMode(BuilderBase.PERFORMANCE_MODE_LOWLATENCY); 153 mPlayer = builder.build(); 154 } catch (PlayerBuilder.BadStateException ex) { 155 Log.e(TAG, "Player - BadStateException" + ex); 156 return StreamBase.ERROR_UNSUPPORTED; 157 } catch (Exception ex) { 158 Log.e(TAG, "Uncaught Error in Player Setup for DuplexAudioManager ex:" + ex); 159 } 160 } 161 162 return StreamBase.OK; 163 } 164 start()165 public int start() { 166 if (LOG) { 167 Log.i(TAG, "start()..."); 168 } 169 int result = StreamBase.OK; 170 if (mRecorder != null && (result = mRecorder.startStream()) != StreamBase.OK) { 171 if (LOG) { 172 Log.i(TAG, " recorder fails result:" + result); 173 } 174 return result; 175 } 176 177 if (mPlayer != null && (result = mPlayer.startStream()) != StreamBase.OK) { 178 if (LOG) { 179 Log.i(TAG, " player fails result:" + result); 180 } 181 return result; 182 } 183 184 if (LOG) { 185 Log.i(TAG, " result:" + result); 186 } 187 return result; 188 } 189 stop()190 public int stop() { 191 if (LOG) { 192 Log.i(TAG, "stop()"); 193 } 194 int playerResult = StreamBase.OK; 195 if (LOG) { 196 Log.i(TAG, " mPlayer:" + mPlayer); 197 } 198 if (mPlayer != null) { 199 int result1 = mPlayer.stopStream(); 200 int result2 = mPlayer.teardownStream(); 201 playerResult = result1 != StreamBase.OK ? result1 : result2; 202 } 203 204 int recorderResult = StreamBase.OK; 205 if (LOG) { 206 Log.i(TAG, " mRecorder:" + mRecorder); 207 } 208 if (mRecorder != null) { 209 int result1 = mRecorder.stopStream(); 210 int result2 = mRecorder.teardownStream(); 211 recorderResult = result1 != StreamBase.OK ? result1 : result2; 212 } 213 214 int ret = playerResult != StreamBase.OK ? playerResult : recorderResult; 215 216 if (LOG) { 217 Log.i(TAG, " returns:" + ret); 218 } 219 return ret; 220 } 221 getNumPlayerBufferFrames()222 public int getNumPlayerBufferFrames() { 223 return mPlayer != null ? mPlayer.getSystemBurstFrames() : 0; 224 } 225 getNumRecorderBufferFrames()226 public int getNumRecorderBufferFrames() { 227 return mRecorder != null ? mRecorder.getSystemBurstFrames() : 0; 228 } 229 getAudioSource()230 public AudioSource getAudioSource() { 231 return mPlayer != null ? mPlayer.getAudioSource() : null; 232 } 233 234 /** 235 * Don't call this until the streams are started 236 * @return true if both player and recorder are routed to the devices specified 237 * with setRecorderRouteDevice() and setPlayerRouteDevice(). 238 */ validateRouting()239 public boolean validateRouting() { 240 if (mPlayerSelectedDevice == null && mRecorderSelectedDevice == null) { 241 return true; 242 } 243 244 if (mPlayer == null || !mPlayer.isPlaying() 245 || mRecorder == null || !mRecorder.isRecording()) { 246 return false; 247 } 248 249 if (mPlayerSelectedDevice != null 250 && mPlayer.getRoutedDeviceId() != mPlayerSelectedDevice.getId()) { 251 return false; 252 } 253 254 if (mRecorderSelectedDevice != null 255 && mRecorder.getRoutedDeviceId() != mRecorderSelectedDevice.getId()) { 256 return false; 257 } 258 259 // Everything checks out OK. 260 return true; 261 } 262 } 263