Un proyecto extenso: transmisión de audio desde el micrófono al dispositivo Android

Quiero tomar la entrada de audio de un micrófono Bluetooth y transmitirla en voz alta, como un megáfono o un simple sistema de megafoníay transmitirlo en tiempo real.

Para este propósito, estoy usando las clases AudioRecord y AudioTrack, ya que MediaRecorder y MediaPlayer escriben y leen en un archivo externo, lo que podría imaginar que retrasa el audio mucho más de lo necesario (¿es esto cierto?).

He descartado la idea de usar cualquier clase de Bluetooth ya que la API de Android no admite el dispositivo Android como un sumidero en un escenario de origen / sumidero (al menos todavía no) y luego requeriría la programación nativa de las bibliotecas de Android (esto es También es cierto, ¿verdad?)

Ahora. La aplicación funciona, peroel retraso es demasiado largo y se supone que la aplicación reproduce audio en tiempo real real.

Mi primera pregunta es: ¿Son AudioRecord / AudioTrack las clases adecuadas para este propósito (transmitir audio a través de un micrófono Bluetooth a los altavoces del dispositivo y reproducirlo en voz alta en tiempo real)?

Y de ser así:¿Cómo puedo disminuir el retraso en el audio? para que transmita el audio de un micrófonoen tiempo real real? El código completo que figura aquí, así que ayúdese:

En Manifiesto, se requiere un permiso para grabar audio.

<uses-permission android:name="android.permission.RECORD_AUDIO" />

Tengo una clase dedicada que extiende un hilo para manejar el audio aquí:

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");
    }
}

}

Y mi clase Main cuenta con botones en los que se puede hacer clic que llaman a los métodos de inicio / detención en la transmisión de audio:

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 se muestran a través de un diseño gráfico con botones 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>

¿Cómo puedo disminuir el retraso en la transmisión de audio?&nbsp;¿Estoy usando las clases correctas, o hay otro / mejor enfoque para este problema?

Además: la entrada de audio no parece provenir del micrófono Bluetooth real, sino del micrófono de hardware de Android, que no es la idea.¿Cómo puedo dirigir la lectura de la entrada de audio al micrófono Bluetooth en lugar de leer la interna?