Как транслировать звук с телефона Android на другой телефон Android с помощью Bluetooth?

Я пытаюсь создать приложение для рации с Bluetooth для одного из моих классов, но не могу заставить его работать правильно. Я изменил пример BluetoothChat (http://developer.android.com/guide/topics/connectivity/bluetooth.html), чтобы попытаться удовлетворить мои потребности, и я могу успешно передавать голосовые записи с одного телефона на другой, но получается очень статично. Я использую AudioRecord и AudioTrack для записи и воспроизведения.

У меня есть эти глобальные переменные

private static final int RECORDER_SAMPLERATE = 8000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

private AudioRecord recorder = null;
private AudioTrack audioTrack = null;
private int bufferSize = 0;
private Thread recordingThread = null;
private boolean isRecording = false;

и в моем методе onCreate я устанавливаю bufferSize и инициализирую AudioTrack.

bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING);

audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 
            RECORDER_SAMPLERATE, AudioFormat.CHANNEL_OUT_STEREO, 
            RECORDER_AUDIO_ENCODING, bufferSize, AudioTrack.MODE_STREAM);

Вот мой код, когда нажата кнопка записи / отправки (в настоящее время у меня есть только для записи 3 секунд данных).

private void startRecording() {
    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            RECORDER_SAMPLERATE, RECORDER_CHANNELS,
            RECORDER_AUDIO_ENCODING, bufferSize);

        int i = recorder.getState();
        if (i == 1)
            recorder.startRecording();

        isRecording = true;

        new Timer().schedule(new TimerTask() {

            @Override
            public void run() {
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() 
                    {
                        enableButtons(false);
                        if(null != recorder){
                                isRecording = false;

                                recorder.stop();
                                recorder.release();

                                recorder = null;
                                recordingThread = null;
                        }
                    }
                });

            }
        }, 3000);

    recordingThread = new Thread(new Runnable() {

        @Override
        public void run() {
            writeOutAudioData();
        }
    }, "AudioRecorder Thread");

    recordingThread.start();
}

private void writeOutAudioData() {

    byte data[] = new byte[bufferSize];

    int read = 0;

        while (isRecording) 
        {
            read = recorder.read(data, 0, bufferSize);

            if (AudioRecord.ERROR_INVALID_OPERATION != read) 
            {
                    mChatService.write(data);
                    //TODO

            }
        }
}

Вот код, который обрабатывает все входящие и исходящие передачи

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType) {
        Log.d(TAG, "create ConnectedThread: " + socketType);
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the BluetoothSocket input and output streams
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "temp sockets not created", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        Log.i(TAG, "BEGIN mConnectedThread");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);

                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothChatService.this.start();
                break;
            }
        }
    }

    /**
     * Write to the connected OutStream.
     * @param buffer  The bytes to write
     */
    public void write(byte[] buffer) {
        try {
            mmOutStream.write(buffer);

            // Share the sent message back to the UI Activity
            mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
        } catch (IOException e) {
            Log.e(TAG, "Exception during write", e);
        }
    }

вот код моего обработчика, который получает информацию от BluetoothChatService

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case MESSAGE_STATE_CHANGE:
            if (D)
                Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
            switch (msg.arg1) {
            case BluetoothChatService.STATE_CONNECTED:
                setStatus(getString(R.string.title_connected_to,
                        mConnectedDeviceName));
                mConversationArrayAdapter.clear();
                break;
            case BluetoothChatService.STATE_CONNECTING:
                setStatus(R.string.title_connecting);
                break;
            case BluetoothChatService.STATE_LISTEN:
            case BluetoothChatService.STATE_NONE:
                setStatus(R.string.title_not_connected);
                break;
            }
            break;
        case MESSAGE_WRITE:
            //byte[] writeBuf = (byte[]) msg.obj;
            break;
        case MESSAGE_READ:
            final byte[] readBuf = (byte[]) msg.obj;
            final byte bytes = (byte) msg.arg1;

            Thread audioPlay = new Thread()
            {
                public void run(){
                    audioTrack.play();
                    audioTrack.write(readBuf, 0, readBuf.length);
                    audioTrack.stop();
                }

            };
            audioPlay.start();
            break;
        }
    }
};

Я просто не понимаю, какой размер буфера byte [] должен быть в классе ConnectedThread. Стоит ли оставить 1024 или поменять на что-нибудь другое? И я не могу понять, как правильно читать эти входящие байты и записывать их в AudioTrack для воспроизведения (без статики). Я использую Android 4.3 на одном телефоне и 4.2.2 на другом. Любая помощь будет принята с благодарностью! Спасибо!


person agarcia_21    schedule 27.02.2014    source источник
comment
нашел работу вокруг   -  person Kaushik    schedule 21.04.2014


Ответы (1)


Я знаю, что прошло около года, но у меня тоже была эта проблема.

Вы должны убедиться, что то, что вы отправляете, - это именно то, что вы хотите отправить, и то, что вы пишете в AudioTrack, - это именно то, что вы получаете. Простой тест - подсчитать количество отправленных байтов и сравнить их с полученными (воспринимаемыми) байтами.

Я считаю, что ключ использует минимальный размер буфера, рассчитанный вашими параметрами для аудиодорожки.

int minsize = AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

Вы можете отправлять данные в любом размере блока, который вы хотите, но вы увидите, что обычно это 1007 байт для гнезда Bluetooth (это то, что я вижу в своих тестах). Я подозреваю, что лучшая производительность будет, если вы позволите сокету разбить на части для вас, так как он все равно будет выполнять проверку, поэтому вы, вероятно, захотите отправить тот же minsize.

Я также рекомендовал бы изменить ваш аудиоформат на моно, если на ваших устройствах нет бинауральных микрофонов?

Признаюсь, я не пытался переварить весь ваш код, но я думаю, что у вас есть ненужные накладные расходы на инкапсуляцию данных в сообщение. Это поток данных, я не думаю, что его нужно инкапсулировать. Я предлагаю использовать BluetoothSocketServer и BluetoothSocket.

Последнее замечание, так как я пишу это для всех, кто сталкивается с этим. Обратите внимание, что AudioTrack.write() в потоковом режиме БЛОКИРУЕТСЯ, если вы не записываете в него данные minsize. Хотя это объясняется на справочном сайте Android, я хочу подчеркнуть его важность, чтобы не тратить время на отладку тупиковых ситуаций.

person Nick    schedule 24.03.2015