Um projeto extenso: Streaming de áudio do microfone para o dispositivo Android
Quero pegar a entrada de áudio de um microfone Bluetooth e transmiti-la em voz alta - como um megafone ou um sistema PA simples -e transmiti-lo em tempo real.
Para esse propósito, estou usando as classes AudioRecord e AudioTrack - uma vez que o MediaRecorder e o MediaPlayer gravam e leem em um arquivo externo, que eu poderia imaginar atrasa o áudio muito mais do que o necessário (isso é verdade?).
Descartar a ideia de usar qualquer classe Bluetooth, pois a API do Android não suporta o dispositivo Android como um coletor em um cenário de origem / coletor (pelo menos ainda não) e exigiria a programação nativa das bibliotecas do Android (isso é também é verdade, certo?)
Agora. O aplicativo está funcionando, maso atraso é muito longo e o aplicativo deve reproduzir áudio em tempo real.
Minha primeira pergunta é: AudioRecord / AudioTrack são as classes certas para esse fim (transmitir áudio através de um microfone Bluetooth para os alto-falantes do dispositivo e reproduzi-lo em voz alta em tempo real)?
E se:Como posso diminuir o atraso no áudio para transmitir o áudio de um microfoneem tempo real? Todo o código listado aqui, por favor, ajude-se:
No manifesto, é necessária uma permissão para gravar o áudio.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Eu tenho uma classe dedicada que estende um Thread para lidar com áudio aqui:
public class AudioStreamer extends Thread {
/**
* @Params:
*/
private int audioSource = MediaRecorder.AudioSource.MIC;
private int sampleRate = 11025;
private int streamType = AudioManager.STREAM_MUSIC;
private int mode = AudioTrack.MODE_STREAM;
p,rivate int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int channelConfigIn = AudioFormat.CHANNEL_IN_MONO;
private int channelConfigOut = AudioFormat.CHANNEL_OUT_MONO;
private int recordSize;
private int trackSize;
private AudioTrack track;
private AudioRecord recorder;
/**
* Initializes the un-initialized params: buffer, bufferSize, track and recorder
* starts recording/playing with AudioRecord and AudioTrack respectively
*/
public AudioStreamer() {
System.out.println("New code!");
recordSize = AudioRecord.getMinBufferSize(sampleRate,
channelConfigIn, audioFormat);
System.out.println("recordSize: "+recordSize);
trackSize = AudioTrack.getMinBufferSize(sampleRate,
channelConfigOut, audioFormat);
System.out.println("trackSize: "+trackSize);
recorder = new AudioRecord(audioSource, sampleRate,
channelConfigIn, audioFormat, recordSize);
if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
track = new AudioTrack(streamType, sampleRate,
channelConfigOut, audioFormat, trackSize, mode);
if (track.getState() == AudioTrack.STATE_INITIALIZED) {
System.out.println("Record and Track initialized");
} else {
System.out.println("Track != init");
}
} else {
System.out.println("Recorder != init");
}
}
/**
* Runs thread--which reads and writes from/to the Android hardware
*/
public void run() {
recorder.startRecording();
track.play();
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING
&& track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
System.out.println("Recorder and track playing");
} else {
System.out.println("Track and recorder != PLAYING");
}
short[] buffer = new short[recordSize];
int audioLenght = 0;
while (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING
&& track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
audioLenght = recorder.read(buffer, 0, recordSize);
track.write(buffer, 0, audioLenght);
}
}
/**
* sets up the AudioManager for bluetooth audio streaming
*/
public void setAudioManager(AudioManager manager) {
manager.setMode(AudioManager.MODE_IN_COMMUNICATION);
// set true and test
manager.setBluetoothScoOn(true);
manager.setSpeakerphoneOn(true);
System.out.println("bluetoothScoOn: " + manager.isBluetoothScoOn()
+ ", bluetoothA2DP: " + manager.isBluetoothA2dpOn()
+", speakerPhone: "+manager.isSpeakerphoneOn());
/**
* Start BluetoothSCO
*/
if (manager.isBluetoothA2dpOn()) {
manager.startBluetoothSco();
System.out.println("BtSco started");
}
}
/**
* Pauses the audio stream
*/
public void pause() {
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
recorder.stop();
}
if (track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
track.pause();
track.flush();
}
if (track.getPlayState() == AudioTrack.PLAYSTATE_PAUSED
&& recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
System.out.println("Stopped");
}
}
}
E minha classe Main é fornecida com botões clicáveis que chamam métodos start / stop no fluxo de áudio:
public class MainActivity extends AppCompatActivity {
private AudioStreamer audioStreamer;
private AudioManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("Program running ... ");
audioStreamer = new AudioStreamer();
manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
audioStreamer.setAudioManager(manager);
}
public void startAudioStreamer(View view) {
audioStreamer.start();
}
public void pauseAudioStreamer(View view) {
audioStreamer.pause();
}
}
Que são exibidos através de um layout gráfico com botões como este:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.android.audiorecordrevisited.MainActivity">
<Button
android:id="@+id/startAudioStreamer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startAudioStreamer"
android:text="Start AudioStreamer" />
<Button
android:id="@+id/stopAudioStreamer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/startAudioStreamer"
android:onClick="pauseAudioStreamer"
android:text="Stop Audio recording" />
</RelativeLayout>
Como posso diminuir o atraso no fluxo de áudio? Estou usando as classes corretas ou existe outra / melhor abordagem para esse problema?
Além disso: a entrada de áudio parece não passar pelo microfone Bluetooth real, mas pelo microfone de hardware Android - o que não é a ideia.Como posso direcionar a leitura da entrada de áudio para o microfone Bluetooth em vez de ler a entrada interna?