peertube-live-streaming/encoder/src/main/java/com/pedro/encoder/input/audio/MicrophoneManager.java

236 lines
7.1 KiB
Java

package com.pedro.encoder.input.audio;
import android.media.AudioFormat;
import android.media.AudioPlaybackCaptureConfiguration;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.pedro.encoder.Frame;
import java.nio.ByteBuffer;
/**
* Created by pedro on 19/01/17.
*/
public class MicrophoneManager {
private final String TAG = "MicrophoneManager";
private static final int BUFFER_SIZE = 4096;
protected AudioRecord audioRecord;
private GetMicrophoneData getMicrophoneData;
private ByteBuffer pcmBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
private byte[] pcmBufferMuted = new byte[BUFFER_SIZE];
protected boolean running = false;
private boolean created = false;
//default parameters for microphone
private int sampleRate = 32000; //hz
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int channel = AudioFormat.CHANNEL_IN_STEREO;
private boolean muted = false;
private AudioPostProcessEffect audioPostProcessEffect;
HandlerThread handlerThread;
private CustomAudioEffect customAudioEffect = new NoAudioEffect();
public MicrophoneManager(GetMicrophoneData getMicrophoneData) {
this.getMicrophoneData = getMicrophoneData;
}
public void setCustomAudioEffect(CustomAudioEffect customAudioEffect) {
this.customAudioEffect = customAudioEffect;
}
/**
* Create audio record
*/
public void createMicrophone() {
createMicrophone(sampleRate, true, false, false);
Log.i(TAG, "Microphone created, " + sampleRate + "hz, Stereo");
}
/**
* Create audio record with params and default audio source
*/
public void createMicrophone(int sampleRate, boolean isStereo, boolean echoCanceler,
boolean noiseSuppressor) {
createMicrophone(MediaRecorder.AudioSource.DEFAULT, sampleRate, isStereo, echoCanceler, noiseSuppressor);
}
/**
* Create audio record with params and selected audio source
* @param audioSource - the recording source. See {@link MediaRecorder.AudioSource} for the recording source definitions.
*/
public void createMicrophone(int audioSource, int sampleRate, boolean isStereo, boolean echoCanceler,
boolean noiseSuppressor) {
this.sampleRate = sampleRate;
if (!isStereo) channel = AudioFormat.CHANNEL_IN_MONO;
audioRecord =
new AudioRecord(audioSource, sampleRate, channel, audioFormat,
getPcmBufferSize());
audioPostProcessEffect = new AudioPostProcessEffect(audioRecord.getAudioSessionId());
if (echoCanceler) audioPostProcessEffect.enableEchoCanceler();
if (noiseSuppressor) audioPostProcessEffect.enableNoiseSuppressor();
String chl = (isStereo) ? "Stereo" : "Mono";
Log.i(TAG, "Microphone created, " + sampleRate + "hz, " + chl);
created = true;
}
/**
* Create audio record with params and AudioPlaybackCaptureConfig used for capturing internal audio
* Notice that you should granted {@link android.Manifest.permission#RECORD_AUDIO} before calling this!
*
* @param config - AudioPlaybackCaptureConfiguration received from {@link android.media.projection.MediaProjection}
*
* @see AudioPlaybackCaptureConfiguration.Builder#Builder(MediaProjection)
* @see "https://developer.android.com/guide/topics/media/playback-capture"
* @see "https://medium.com/@debuggingisfun/android-10-audio-capture-77dd8e9070f9"
*/
public void createInternalMicrophone(AudioPlaybackCaptureConfiguration config, int sampleRate, boolean isStereo) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
this.sampleRate = sampleRate;
if (!isStereo) channel = AudioFormat.CHANNEL_IN_MONO;
audioRecord = new AudioRecord.Builder()
.setAudioPlaybackCaptureConfig(config)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setChannelMask(channel)
.build())
.setBufferSizeInBytes(getPcmBufferSize())
.build();
audioPostProcessEffect = new AudioPostProcessEffect(audioRecord.getAudioSessionId());
String chl = (isStereo) ? "Stereo" : "Mono";
Log.i(TAG, "Internal microphone created, " + sampleRate + "hz, " + chl);
created = true;
} else createMicrophone(sampleRate, isStereo, false, false);
}
/**
* Start record and get data
*/
public synchronized void start() {
init();
handlerThread = new HandlerThread(TAG);
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
while (running) {
Frame frame = read();
if (frame != null) {
getMicrophoneData.inputPCMData(frame);
} else {
running = false;
}
}
}
});
}
private void init() {
if (audioRecord != null) {
audioRecord.startRecording();
running = true;
Log.i(TAG, "Microphone started");
} else {
Log.e(TAG, "Error starting, microphone was stopped or not created, "
+ "use createMicrophone() before start()");
}
}
public void mute() {
muted = true;
}
public void unMute() {
muted = false;
}
public boolean isMuted() {
return muted;
}
/**
* @return Object with size and PCM buffer data
*/
private Frame read() {
pcmBuffer.rewind();
int size = audioRecord.read(pcmBuffer, pcmBuffer.remaining());
if (size <= 0) {
return null;
}
return new Frame(muted ? pcmBufferMuted : customAudioEffect.process(pcmBuffer.array()),
muted ? 0 : pcmBuffer.arrayOffset(), size);
}
/**
* Stop and release microphone
*/
public synchronized void stop() {
running = false;
created = false;
if (handlerThread != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
handlerThread.quitSafely();
} else {
handlerThread.quit();
}
}
if (audioRecord != null) {
audioRecord.setRecordPositionUpdateListener(null);
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
if (audioPostProcessEffect != null) {
audioPostProcessEffect.releaseEchoCanceler();
audioPostProcessEffect.releaseNoiseSuppressor();
}
Log.i(TAG, "Microphone stopped");
}
/**
* Get PCM buffer size
*/
private int getPcmBufferSize() {
int pcmBufSize =
AudioRecord.getMinBufferSize(sampleRate, channel, AudioFormat.ENCODING_PCM_16BIT);
return pcmBufSize * 5;
}
public int getMaxInputSize() {
return BUFFER_SIZE;
}
public int getSampleRate() {
return sampleRate;
}
public void setSampleRate(int sampleRate) {
this.sampleRate = sampleRate;
}
public int getAudioFormat() {
return audioFormat;
}
public int getChannel() {
return channel;
}
public boolean isRunning() {
return running;
}
public boolean isCreated() {
return created;
}
}