0

我在 android 上采样音频,并基于此将一些 RGB 值发送到使用蓝牙的 arduino 设备。

发送的音频样本和 arduino 做出反应之间有很长的延迟(几秒钟)。我假设这是由于 android 比 arduino 快得多,并且在传输字节被备份到手机缓冲区的地方进行了某种流控制。连接蓝牙的代码是:

mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();

然后发送数据:

mmOutputStream.write(0xFF);
mmOutputStream.write(outputFreq);
mmOutputStream.write(outputMagnitude);

我不介意丢失数据,因为我只需要发送最新的值。

实现这一目标的最佳方法是什么?我是 Android 编程的新手,希望它能够很快工作,所以更简单的解决方案会更好!我已经考虑过某种堆栈,以及一个在计时器上运行的单独线程,它会浏览堆栈顶部并仅发送这些值,但这听起来很复杂,因为我对线程编程一无所知。

有没有办法配置outputstream它只是丢弃没有及时发送的数据?

如果有任何帮助,这是完整的代码:

package com.example.fft1;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import com.androidplot.series.XYSeries;
import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.SimpleXYSeries;


import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
import android.graphics.Color;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity  {

    XYPlot plot;
    SimpleXYSeries plotSeries;
    AudioRecord audioRecord;
    RecordAudio recordTask;
    int frequency = 44100;
    int channelConfiguration = AudioFormat.CHANNEL_IN_MONO;
    int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;


    BluetoothAdapter mBluetoothAdapter;
    BluetoothSocket mmSocket;
    BluetoothDevice mmDevice;
    OutputStream mmOutputStream;
    InputStream mmInputStream;

    int counter;


    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // initialize our XYPlot reference:
        plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
        plot.setRangeBoundaries(-1000000, 1000000, BoundaryMode.FIXED);
        Number[] seriesData = {1,2,3,4,5,6};
        // Turn the above arrays into XYSeries':
        plotSeries = new SimpleXYSeries(
                Arrays.asList(seriesData),          // SimpleXYSeries takes a List so turn our array into a List
                SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value
                "Series1");                             // Set the display title of the series


        // Create a formatter to use for drawing a series using LineAndPointRenderer:
        @SuppressWarnings("deprecation")
        LineAndPointFormatter series1Format = new LineAndPointFormatter(
                Color.rgb(0, 200, 0),                   // line color
                Color.rgb(0, 100, 0),                   // point color
                null);                                  // fill color (none)

        // add a new series' to the xyplot:
        plot.addSeries(plotSeries, series1Format);

        // reduce the number of range labels
        plot.setTicksPerRangeLabel(3);

        // by default, AndroidPlot displays developer guides to aid in laying out your plot.
        // To get rid of them call disableAllMarkup():
        plot.disableAllMarkup();

        Button startBtn = (Button)findViewById(R.id.startButton);

        int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
        audioRecord = new AudioRecord(
                MediaRecorder.AudioSource.DEFAULT, 
                frequency,
                channelConfiguration, 
                audioEncoding, 
                bufferSize
                );


        startBtn.setOnClickListener(new startBtnClick());

        Button connectBtn = (Button)findViewById(R.id.connectBtn);
        connectBtn.setOnClickListener(new connectBtnClick());


    }

    class startBtnClick implements Button.OnClickListener {
        @Override
        public void onClick(View view) {
            Button button = (Button) view;
            if (button.getText().toString().equals("Start")) {
                button.setText("Stop");
                recordTask  = new RecordAudio();
                recordTask.execute();
            } else {
                button.setText("Start");
                recordTask.cancel(false);
            }
        }

    }


    class connectBtnClick implements Button.OnClickListener {
        @Override
        public void onClick(View view) {

            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

            if(!mBluetoothAdapter.isEnabled()) {
                Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBluetooth, 0);
            }

            Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
            if(pairedDevices.size() > 0) {
                for(BluetoothDevice device : pairedDevices) {
                    Log.v("BT2", "Device: " + device.getName());
                    if(device.getName().equals("linvor")) {
                        mmDevice = device;
                        break;
                    }
                }
            }

            UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); //Standard SerialPortService ID
            try {
                mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);

                mmSocket.connect();
                mmOutputStream = mmSocket.getOutputStream();
                mmInputStream = mmSocket.getInputStream();

                for (int i = 0; i < 255; i++) {
                    mmOutputStream.write(0xFF);
                    mmOutputStream.write(i);
                    mmOutputStream.write(255);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 

        }
        //beginListenForData();
    }


    private class RecordAudio extends AsyncTask<Void, Integer[], Void> {
        @Override
        protected Void doInBackground(Void... params) {


            int blockSize = 128;
            short[] buffer = new short[blockSize];
            double[] bufferD = new double[blockSize];
            audioRecord.startRecording();

            // Here's the Fast Fourier Transform from JTransforms
            DoubleFFT_1D fft = new DoubleFFT_1D(buffer.length);


            while (!isCancelled()) {
                counter = (counter + 1) % 1000;
                //Log.v("FFT1", String.valueOf(counter));
                int sumEnergy = 0;
                logTime("start");
                // Read audio to 'samples' array and convert it to double[]
                audioRecord.read(buffer, 0, buffer.length);
                logTime("after reading");

                for (int i = 0; i < buffer.length; i++) {
                    bufferD[i]=buffer[i];
                }

                fft.realForward(bufferD);
                logTime("after fft");
                Integer[] spectrum = new Integer[blockSize/2];
                for (int k = 0; k < blockSize / 2; k++) {
                    spectrum[k] = new Integer((int) Math.sqrt( (bufferD[2*k] * bufferD[2*k]) + (bufferD[2*k+1] * bufferD[2*k+1]) ));   
                }

                int averageMagnitude = 0;
                int middleFreqBin = 0;
                for (int i = 0; i < spectrum.length; i++) {
                    averageMagnitude += spectrum[i];
                }
                averageMagnitude /= spectrum.length;

                int halfMagnitudeSum = 0;
                for (int i = 0; i < spectrum.length / 2; i++) {
                    halfMagnitudeSum += spectrum[i] * i;
                }
                halfMagnitudeSum /= 2;
                int runningTotal = 0;
                for (int i = 0; i < spectrum.length; i++) {
                    runningTotal += spectrum[i] * i;
                    if (runningTotal > halfMagnitudeSum) {
                        middleFreqBin = i;
                        break;
                    }
                }

                int outputMagnitude = map(averageMagnitude, 0, 50000, 0, 254);
                int outputFreq = map(middleFreqBin, 0, spectrum.length, 0, 254);
                if (outputMagnitude > 254) outputMagnitude = 254;

                try {
                    //Log.v("FFT1", "OutputFreq: " + outputFreq + ", outputMagnitude: " + outputMagnitude);
                    mmOutputStream.write(0xFF);
                    mmOutputStream.write(outputFreq);
                    mmOutputStream.write(outputMagnitude);
                    Thread.sleep(10);

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    Log.v("FFT1","Not connected");
                }               

                logTime("after bluetooth");

                publishProgress(spectrum);

            }
            return null;  

        }

        protected void onCancelled() {
            audioRecord.stop();
        }

        protected void onProgressUpdate(Integer[]... args) {
            Integer[] spectrum = args[0];

            plotSeries.setModel(Arrays.asList(spectrum),  SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
            plot.redraw();
        }

        int map(int x, int in_min, int in_max, int out_min, int out_max)    {
            return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public void logTime(String text) {

        if (counter < 5) {
            String time =  String.valueOf(new java.util.Date().getTime());
            Log.v("FFT1", text + ": " + time.substring(time.length()-4, time.length()));
        }
    }

}
4

1 回答 1

0

The OutputStream.write(); lines returned immediately, but the amount of delay increased over time. Clearly data was getting backed up somewhere in the bluetooth stack, which was why I put in the Thread.sleep(10), to try and slow things down.

That caused other problems around blocking though, and I replaced it with a couple of lines that check when the last write() was before sending any new data. If it was less than a configured time (call it timeDelay), then it skips the new write(). Manually tuning the value of timeDelay then enabled me to avoid flooding the bluetooth stack. For in

于 2013-08-02T14:10:33.623 回答