• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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