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