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.recorder;
17 
18 import android.media.AudioFormat;
19 import android.media.AudioRecord;
20 
21 import org.hyphonate.megaaudio.common.StreamBase;
22 
23 public abstract class Recorder extends StreamBase {
24     private static final String TAG = Recorder.class.getSimpleName();
25 
26     protected AudioSinkProvider mSinkProvider;
27 
28     // Recording state
29     /**
30      * <code>true</code> if currently recording audio data
31      */
32     protected boolean mRecording = false;
33 
34     //
35     // Recorder-specific attributes
36     //
37     // This value is to indicate that no explicit call to set an input preset in the builder
38     // will be made.
39     // Constants can be found here:
40     // https://developer.android.com/reference/android/media/MediaRecorder.AudioSource
41     // or preferentially in oboe/Definitions.h
42     int mInputPreset = INPUT_PRESET_NONE;
43     public static final int INPUT_PRESET_NONE = -1;
44     public static final int INPUT_PRESET_DEFAULT = 0;
45     public static final int INPUT_PRESET_GENERIC = 1;
46     public static final int INPUT_PRESET_VOICE_UPLINK = 2;
47     public static final int INPUT_PRESET_VOICE_DOWNLINK = 3;
48     public static final int INPUT_PRESET_VOICE_CALL = 4;
49     public static final int INPUT_PRESET_CAMCORDER = 5;
50     public static final int INPUT_PRESET_VOICERECOGNITION = 6;
51     public static final int INPUT_PRESET_VOICECOMMUNICATION = 7;
52     public static final int INPUT_PRESET_REMOTE_SUBMIX = 8;
53     public static final int INPUT_PRESET_UNPROCESSED = 9;
54     public static final int INPUT_PRESET_VOICEPERFORMANCE = 10;
55 
Recorder(AudioSinkProvider sinkProvider)56     public Recorder(AudioSinkProvider sinkProvider) {
57         mSinkProvider = sinkProvider;
58     }
59 
60     // It is not clear that this can be set post-create for Java
61     // public abstract void setInputPreset(int preset);
62 
63     /*
64      * State
65      */
isRecording()66     public boolean isRecording() {
67         return mRecording;
68     }
69 
70     /*
71      * Utilities
72      */
73     public static final int AUDIO_CHANNEL_COUNT_MAX = 30;
74 
75     public static final int AUDIO_CHANNEL_REPRESENTATION_POSITION   = 0x0;
76     public static final int AUDIO_CHANNEL_REPRESENTATION_INDEX      = 0x2;
77 
78     //
79     // Attributes
80     //
81     /**
82      * Calculate the optimal buffer size for the specified channel count and sample rate
83      * @param channelCount number of channels of audio data in record buffers
84      * @param sampleRate sample rate of recorded data
85      * @return The minimal buffer size to avoid overruns in the recording stream.
86      */
calcMinBufferFramesStatic(int channelCount, int sampleRate)87     public static int calcMinBufferFramesStatic(int channelCount, int sampleRate) {
88         int channelMask = Recorder.channelCountToChannelMask(channelCount);
89         int bufferSizeInBytes =
90                 AudioRecord.getMinBufferSize (sampleRate,
91                         channelMask,
92                         AudioFormat.ENCODING_PCM_FLOAT);
93         return bufferSizeInBytes / sampleSizeInBytes(AudioFormat.ENCODING_PCM_FLOAT);
94     }
95 
96     /*
97      * Channel Utils
98      */
99     // TODO - Consider moving these into a "Utilities" library
100     /* Not part of public API */
audioChannelMaskFromRepresentationAndBits(int representation, int bits)101     private static int audioChannelMaskFromRepresentationAndBits(int representation, int bits)
102     {
103         return ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits);
104     }
105 
106     /* Derive a channel mask for index assignment from a channel count.
107      * Returns the matching channel mask,
108      * or AUDIO_CHANNEL_NONE if the channel count is zero,
109      * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX.
110      */
audioChannelMaskForIndexAssignmentFromCount(int channel_count)111     private static int audioChannelMaskForIndexAssignmentFromCount(int channel_count)
112     {
113         if (channel_count == 0) {
114             return 0; // AUDIO_CHANNEL_NONE
115         }
116         if (channel_count > AUDIO_CHANNEL_COUNT_MAX) {
117             return AudioFormat.CHANNEL_INVALID;
118         }
119         int bits = (1 << channel_count) - 1;
120         return audioChannelMaskFromRepresentationAndBits(AUDIO_CHANNEL_REPRESENTATION_INDEX, bits);
121     }
122 
123     /**
124      * @param channelCount  The number of channels for which to generate an input position mask.
125      * @return An input channel-position mask corresponding the supplied number of channels.
126      */
channelCountToChannelMask(int channelCount)127     public static int channelCountToChannelMask(int channelCount) {
128         int bits;
129         switch (channelCount) {
130             case 1:
131                 bits = AudioFormat.CHANNEL_IN_MONO;
132                 break;
133 
134             case 2:
135                 bits = AudioFormat.CHANNEL_IN_STEREO;
136                 break;
137 
138             case 3:
139             case 4:
140             case 5:
141             case 6:
142             case 7:
143             case 8:
144                 // FIXME FCC_8
145                 return audioChannelMaskForIndexAssignmentFromCount(channelCount);
146 
147             default:
148                 return AudioFormat.CHANNEL_INVALID;
149         }
150 
151         return audioChannelMaskFromRepresentationAndBits(
152                 AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
153     }
154 }
155