问题
I want to take audio input from a Bluetooth microphone and stream it out loud--like a megaphone or a simple PA system--and broadcast it in real-time.
For this purpose I am using the AudioRecord and AudioTrack classes--since MediaRecorder and MediaPlayer writes and reads to an external file, which I could imagine delays the audio much more than necessary (is this true?).
I have discarded the idea of using any Bluetooth classes since the Android API doesn't support the Android device as a Sink in a Source/Sink-scenario (at least not yet) and would then require native programming of the Android libraries (this is also true, right?)
Now. The app is working, but the delay is way too long and the app is suppose to play audio in actual real-time.
My first question is: Are AudioRecord/AudioTrack the right classes for this purpose (streaming audio through a Bluetooth microphone to the device speakers and play it out loud in real-time)?
And if so: How can I diminish the delay on the audio so that it streams the audio from a microphone in actual real-time? The whole code listed here, so please help yourself:
In Manifest, a permission for recording audio is required.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
I have a dedicated class which extends a Thread for handling audio here:
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;
private 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");
}
}
}
And my Main class is provided with clickable buttons that call start/stop-methods on the audio stream:
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();
}
}
Which are displayed through a graphical layout with buttons like this:
<?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>
How can I diminish the delay on the audio stream? Am i using the correct classes, or is there another/better approach to this problem?
Also: The audio input doesn't seem to come through the actual Bluetooth microphone but instead from the Android hardware microphone--which isn't the idea. How can I direct reading the audio input to the Bluetooth microphone instead of reading off the internal one?
来源:https://stackoverflow.com/questions/40998860/an-extensive-project-streaming-audio-from-microphone-to-android-device