一个广泛的项目:从麦克风到Android设备的流媒体音频

时间:2016-12-06 15:22:53

标签: android bluetooth audio-streaming android-bluetooth android-audiorecord

我想从蓝牙麦克风输入音频并大声输出 - 如扩音器或简单的扩声系统 - 并实时广播

为此目的,我正在使用AudioRecord和AudioTrack类 - 因为MediaRecorder和MediaPlayer会写入和读取外部文件,我可以想象这会使音频延迟得多(这是真的吗?)。

我已经放弃了使用任何蓝牙类的想法,因为Android API并不支持Android设备作为源/接收器场景中的接收器(至少现在还没有),然后需要本机编程Android库(这也是事实,对吧?)

立即。该应用正在运行,但延迟太长,并且该应用假设实际播放音频。

我的第一个问题是:AudioRecord / AudioTrack是否适用于此目的(通过蓝牙麦克风将音频流式传输到设备扬声器并实时播放)?

如果是这样的话:如何减少音频的延迟,以便它以实时实时方式从麦克风流式传输音频?此处列出的整个代码,请自己帮忙:

在Manifest中,需要录制音频的权限。

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

我有一个专门的类,它扩展了一个用于处理音频的线程:

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

}

我的Main类提供了可点击的按钮,可以在音频流上调用start / stop-methods:

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

}

通过带有如下按钮的图形布局显示:

<?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>

如何减少音频流的延迟?我使用的是正确的类,还是有其他/更好的方法解决这个问题?

另外:音频输入似乎不是来自实际的蓝牙麦克风,而是来自Android硬件麦克风 - 这不是想法。 如何直接将音频输入读取到蓝牙麦克风而不是读取内置麦克风呢?

0 个答案:

没有答案