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     private static final String TAG = DuplexAudioManager.class.getSimpleName();
33 
34     // Player
35     //TODO - explain these constants
36     private int mNumPlayerChannels = 2;
37     private int mPlayerSampleRate = 48000;
38     private int mNumPlayerBufferFrames;
39 
40     private Player mPlayer;
41     private AudioSourceProvider mSourceProvider;
42     private AudioDeviceInfo mPlayerSelectedDevice;
43 
44     // Recorder
45     private int mNumRecorderChannels = 2;
46     private int mRecorderSampleRate = 48000;
47     private int mNumRecorderBufferFrames;
48 
49     private Recorder mRecorder;
50     private AudioSinkProvider mSinkProvider;
51     private AudioDeviceInfo mRecorderSelectedDevice;
52     private int mInputPreset = Recorder.INPUT_PRESET_NONE;
53 
DuplexAudioManager(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider)54     public DuplexAudioManager(AudioSourceProvider sourceProvider, AudioSinkProvider sinkProvider) {
55         mSourceProvider = sourceProvider;
56         mSinkProvider = sinkProvider;
57     }
58 
getPlayer()59     public Player getPlayer() {
60         return mPlayer;
61     }
62 
getRecorder()63     public Recorder getRecorder() {
64         return mRecorder;
65     }
66 
setNumRecorderChannels(int numChannels)67     public void setNumRecorderChannels(int numChannels) {
68         mNumRecorderChannels = numChannels;
69     }
70 
setInputPreset(int preset)71     public void setInputPreset(int preset) { mInputPreset = preset; }
72 
setupStreams(int playerType, int recorderType)73     public int setupStreams(int playerType, int recorderType) {
74         // Recorder
75         if ((recorderType & BuilderBase.TYPE_MASK) != BuilderBase.TYPE_NONE) {
76             try {
77                 mRecorder = new RecorderBuilder()
78                         .setRecorderType(recorderType)
79                         .setAudioSinkProvider(mSinkProvider)
80                         .build();
81                 if (mInputPreset != Recorder.INPUT_PRESET_NONE) {
82                     mRecorder.setInputPreset(mInputPreset);
83                 }
84                 mRecorder.setRouteDevice(mRecorderSelectedDevice);
85                 int errorCode = mRecorder.setupStream(
86                         mNumRecorderChannels, mRecorderSampleRate, mNumRecorderBufferFrames);
87                 if (errorCode != StreamBase.OK) {
88                     Log.e(TAG, "Recorder setupStream() failed code: " + errorCode);
89                     return errorCode;
90                 }
91                 mNumRecorderBufferFrames = mRecorder.getNumBufferFrames();
92             } catch (RecorderBuilder.BadStateException ex) {
93                 Log.e(TAG, "Recorder - BadStateException" + ex);
94                 return StreamBase.ERROR_UNSUPPORTED;
95             }
96         }
97 
98         // Player
99         if ((playerType & BuilderBase.TYPE_MASK) != BuilderBase.TYPE_NONE) {
100             try {
101                 mNumPlayerBufferFrames =
102                         Player.calcMinBufferFrames(mNumPlayerChannels, mPlayerSampleRate);
103                 mPlayer = new PlayerBuilder()
104                         .setPlayerType(playerType)
105                         .setSourceProvider(mSourceProvider)
106                         .build();
107                 mPlayer.setRouteDevice(mPlayerSelectedDevice);
108                 int errorCode = mPlayer.setupStream(
109                         mNumPlayerChannels, mPlayerSampleRate, mNumPlayerBufferFrames);
110                 if (errorCode != StreamBase.OK) {
111                     Log.e(TAG, "Player - setupStream() failed code: " + errorCode);
112                     return errorCode;
113                 }
114             } catch (PlayerBuilder.BadStateException ex) {
115                 Log.e(TAG, "Player - BadStateException" + ex);
116                 return StreamBase.ERROR_UNSUPPORTED;
117             }
118         }
119 
120         return StreamBase.OK;
121     }
122 
start()123     public int start() {
124         int result = StreamBase.OK;
125         if (mRecorder != null && (result = mRecorder.startStream()) != StreamBase.OK) {
126             return result;
127         }
128 
129         if (mPlayer != null && (result = mPlayer.startStream()) != StreamBase.OK) {
130             return result;
131         }
132 
133         return result;
134     }
135 
stop()136     public int stop() {
137         int playerResult = StreamBase.OK;
138         if (mPlayer != null) {
139            int result1 = mPlayer.stopStream();
140            int result2 = mPlayer.teardownStream();
141            playerResult = result1 != StreamBase.OK ? result1 : result2;
142         }
143 
144         int recorderResult = StreamBase.OK;
145         if (mRecorder != null) {
146             int result1 = mRecorder.stopStream();
147             int result2 = mRecorder.teardownStream();
148             recorderResult = result1 != StreamBase.OK ? result1 : result2;
149         }
150 
151         return playerResult != StreamBase.OK ? playerResult: recorderResult;
152     }
153 
getNumPlayerBufferFrames()154     public int getNumPlayerBufferFrames() {
155         return mPlayer != null ? mPlayer.getNumBufferFrames() : 0;
156     }
157 
getNumRecorderBufferFrames()158     public int getNumRecorderBufferFrames() {
159         return mRecorder != null ? mRecorder.getNumBufferFrames() : 0;
160     }
161 
getAudioSource()162     public AudioSource getAudioSource() {
163         return mPlayer != null ? mPlayer.getAudioSource() : null;
164     }
165 }
166