java - AudioRecord 在 Android 5.01 上产生零间隙

标签 java android android-5.0-lollipop zero audiorecord

我尝试使用 AudioRecord 编写一个测试应用程序来录制几秒钟的音频以显示在屏幕上。但是,我似乎得到了如下所示的零值区域的重复模式。我不确定这是正常行为还是我的代码中的错误。

zeros

主 Activity .java

public class MainActivity extends Activity implements OnClickListener 
{
    private static final int SAMPLE_RATE = 44100;
    private Button recordButton, playButton;
    private String filePath;
    private boolean recording;
    private AudioRecord record;
    private short[] data;
    private TestView testView;

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

        Button recordButton = (Button) this.findViewById(R.id.recordButton);
        recordButton.setOnClickListener(this);

        Button playButton = (Button)findViewById(R.id.playButton);
        playButton.setOnClickListener(this);

        FrameLayout frame = (FrameLayout)findViewById(R.id.myFrame);
        frame.addView(testView = new TestView(this));
    }

    @Override
    public void onClick(View v) 
    {
        if(v.getId() == R.id.recordButton)
        {
            if(!recording)
            {
                int bufferSize = AudioRecord.getMinBufferSize(  SAMPLE_RATE,
                                                                AudioFormat.CHANNEL_IN_MONO,
                                                                AudioFormat.ENCODING_PCM_16BIT);

                record = new AudioRecord(   MediaRecorder.AudioSource.MIC,
                                            SAMPLE_RATE,
                                            AudioFormat.CHANNEL_IN_MONO,
                                            AudioFormat.ENCODING_PCM_16BIT,
                                            bufferSize * 2);

                data = new short[10 * SAMPLE_RATE]; // Records up to 10 seconds

                new Thread()
                {
                    @Override
                    public void run() 
                    {
                        recordAudio();
                    }

                }.start();

                recording = true;

                Toast.makeText(this, "recording...", Toast.LENGTH_SHORT).show();
            }
            else
            {
                recording = false;
                Toast.makeText(this, "finished", Toast.LENGTH_SHORT).show();
            }
        }
        else if(v.getId() == R.id.playButton)
        {   
            testView.invalidate();
            Toast.makeText(this, "play/pause", Toast.LENGTH_SHORT).show();
        }
    }

    void recordAudio()
    {
        record.startRecording();
        int index = 0;
        while(recording)
        {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            int result = record.read(data, index, SAMPLE_RATE); // read 1 second at a time
            if(result == AudioRecord.ERROR_INVALID_OPERATION || result == AudioRecord.ERROR_BAD_VALUE)
            {
                App.d("SOME SORT OF RECORDING ERROR MATE");
                return;
            }
            else
            {
                index += result; // increment by number of bytes read
                App.d("read: "+result);
            }
        }
        record.stop();
        data = Arrays.copyOf(data, index);

        testView.setData(data);
    }

    @Override
    protected void onPause() 
    {

        super.onPause();
    }
}

测试 View .java

public class TestView extends View 
{
    private short[] data;
    Paint paint = new Paint();
    Path path = new Path();
    float min, max;

    public TestView(Context context) 
    {
        super(context);

        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(1);
        paint.setStyle(Style.FILL_AND_STROKE);
    }

    void setData(short[] data)
    {
        min = Short.MAX_VALUE;
        max = Short.MIN_VALUE;
        this.data = data;
        for(int i = 0; i < data.length; i++)
        {
            if(data[i] < min)
                min = data[i];

            if(data[i] > max)
                max = data[i];
        }
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        canvas.drawRGB(255, 255, 255);
        if(data != null)
        {
            float interval = (float)this.getWidth()/data.length;
            for(int i = 0; i < data.length; i+=10)
                canvas.drawCircle(i*interval,(data[i]-min)/(max - min)*this.getHeight(),5 ,paint);

        }
        super.onDraw(canvas);
    }
}

最佳答案

您的导航栏图标看起来像是您可能在 Android 5 上运行,而 Android 5.0 版本中存在一个错误,这可能正是您所看到的问题的原因。

短裤记录在 L 预览中给出了错误的返回值,并且在修复过程中大量修改代码时,他们在 5.0 版本中错误地将偏移量参数加倍。您的代码将索引增加它在每次调用中读取的(正确的)数量,但是音频内部的指针数学错误将使您传递的偏移量加倍,这意味着每个记录周期结束后跟一个相等的未写入周期 -缓冲,您将其视为零间隙。

此问题已在 http://code.google.com/p/android/issues/detail?id=80866 上报告

去年秋天那个时候提交的补丁被拒绝了,因为他们说他们已经在内部处理了。查看 AOSP 5.1 的 git 历史记录,这似乎是内部提交 283a9d9e1 11 月 13 日,当我当月晚些时候遇到它时,它还没有公开。虽然我还没有在 5.1 上尝试过这个,但似乎应该修复它,所以它很可能在 5.0-5.02 中被破坏(并且在 L 预览中以不同的方式)但在 4.4 和更早版本中也能正常工作与 5.1 及更高版本一样。

在损坏和未损坏的发行版本之间保持一致行为的最简单解决方法是避免在记录短裤时传递非零偏移量 - 这就是我在遇到问题时修复程序的方法。一个更复杂的想法是尝试弄清楚您是否使用损坏的版本,如果是,则将传递的参数减半。一种方法是检测设备版本,但可以想象某些供应商或自定义 ROM 5.0 版本可能已打补丁,因此您可以更进一步,使用测试偏移量对零缓冲区进行简短记录,然后将其扫描到查看非零数据实际开始的位置。

关于java - AudioRecord 在 Android 5.01 上产生零间隙,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29700672/

相关文章:

android - 如何在 Lollipop 5.0 的 WebView 中设置代理

Java: `static` 方法

android - Lokhttp3/internal/Util 类中没有静态方法 delimiterOffset

java - 我正在尝试在 java 中运行 groovy 脚本

java - 使用 Order By DateTime 获取 SQLite 数据但不起作用

android - 为什么 AVD Manager 中缺少 Intel x86 Atom System Image (API 15)

android - 文件上传字段在 Android 的 Web View 中不起作用

android - Android L 中的警报对话框按钮问题

java - 使用 TLS 时通过 LDAP 进行身份验证失败

java - MongoDB Java CodecConfigurationException 找不到公共(public)构造函数