3

我有一个问题,当我录制音频时,我点击“播放”按钮,它只播放大约 3 秒,而不是完全录制的音频。我尝试增加缓冲区大小,但它反而使我的应用程序崩溃。任何人都可以建议吗?谢谢

Below is my code
package com.exercise.AndroidAudioRecord;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;

public class AndroidAudioRecordActivity extends Activity {

    String[] freqText = {"11.025 KHz (Lowest)", "16.000 KHz", "22.050 KHz", "44.100 KHz (Highest)"};
    Integer[] freqset = {11025, 16000, 22050, 44100};
    private ArrayAdapter<String> adapter;

    Spinner spFrequency;
    Button startRec, stopRec, playBack;

    Boolean recording;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        startRec = (Button)findViewById(R.id.startrec);
        stopRec = (Button)findViewById(R.id.stoprec);
        playBack = (Button)findViewById(R.id.playback);

        startRec.setOnClickListener(startRecOnClickListener);
        stopRec.setOnClickListener(stopRecOnClickListener);
        playBack.setOnClickListener(playBackOnClickListener);

        spFrequency = (Spinner)findViewById(R.id.frequency);
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, freqText);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spFrequency.setAdapter(adapter);

        stopRec.setEnabled(false);
    }

    OnClickListener startRecOnClickListener
    = new OnClickListener(){

        @Override
        public void onClick(View arg0) {

            Thread recordThread = new Thread(new Runnable(){

                @Override
                public void run() {
                    recording = true;
                    startRecord();
                }

            });

            recordThread.start();
            startRec.setEnabled(false);
            stopRec.setEnabled(true);

        }};

    OnClickListener stopRecOnClickListener
    = new OnClickListener(){

        @Override
        public void onClick(View arg0) {
            recording = false;
            startRec.setEnabled(true);
            stopRec.setEnabled(false);
        }};

    OnClickListener playBackOnClickListener
        = new OnClickListener(){

            @Override
            public void onClick(View v) {
                playRecord();
            }

    };

    private void startRecord(){

        File file = new File(Environment.getExternalStorageDirectory(), "test.pcm"); 

        int selectedPos = spFrequency.getSelectedItemPosition();
        int sampleFreq = freqset[selectedPos];

        final String promptStartRecord = 
                "startRecord()\n"
                + file.getAbsolutePath() + "\n"
                + (String)spFrequency.getSelectedItem();

        runOnUiThread(new Runnable(){

            @Override
            public void run() {
                Toast.makeText(AndroidAudioRecordActivity.this, 
                        promptStartRecord, 
                        Toast.LENGTH_LONG).show();
            }});

        try {
            file.createNewFile();

            OutputStream outputStream = new FileOutputStream(file);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
            DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);

            int minBufferSize = AudioRecord.getMinBufferSize(sampleFreq, 
                    AudioFormat.CHANNEL_IN_STEREO, 
                    AudioFormat.ENCODING_PCM_16BIT);

            short[] audioData = new short[minBufferSize];

            AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
                    sampleFreq,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    minBufferSize);

            audioRecord.startRecording();

            while(recording){
                int numberOfShort = audioRecord.read(audioData, 0, minBufferSize);
                for(int i = 0; i < numberOfShort; i++){
                    dataOutputStream.writeShort(audioData[i]);
                }
            }

            audioRecord.stop();
            audioRecord.release();
            dataOutputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }




    void playRecord(){

        File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

        int shortSizeInBytes = Short.SIZE/Byte.SIZE;
        int bufferSizeInBytes = (int)(file.length()/shortSizeInBytes);

        short[] audioData = new short[bufferSizeInBytes];
        //int  buflen=bufferSizeInBytes/2; 
        try {
            InputStream inputStream = new FileInputStream(file);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

            int i = 0;
            while(dataInputStream.available() > 0){
                audioData[i] = dataInputStream.readShort();
                i++;
            }

            dataInputStream.close();

            int selectedPos = spFrequency.getSelectedItemPosition();
            int sampleFreq = freqset[selectedPos];
            int sampleFreq1 = 12000;
            int sampleFreq2 = 15000;
            final String promptPlayRecord = 
                    "PlayRecord()\n"
                    + file.getAbsolutePath() + "\n"
                    + (String)spFrequency.getSelectedItem();

            Toast.makeText(AndroidAudioRecordActivity.this, 
                    promptPlayRecord, 
                    Toast.LENGTH_LONG).show();

            AudioTrack audioTrack = new AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    sampleFreq1,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    bufferSizeInBytes,
                    AudioTrack.MODE_STREAM);

            int stereo=audioTrack.setStereoVolume(0.0f, 1.0f);

            audioTrack.write(audioData, 0, bufferSizeInBytes);
            audioTrack.play();


            AudioTrack audioTrack2 = new AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    sampleFreq2,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    bufferSizeInBytes,
                    AudioTrack.MODE_STREAM);

            int stereo2=audioTrack.setStereoVolume(1.0f, 0.0f);

            audioTrack2.write(audioData, 0, bufferSizeInBytes);
            audioTrack2.play();




        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

谢谢大家!

4

3 回答 3

3

首先,我假设录制的 PCM 文件具有预期的大小,这使得您转储的所有与录制文件相关的代码都无关紧要。

我可以在播放代码中看到一个问题,该问题将导致播放长度为预期长度的 1/2。

int shortSizeInBytes = Short.SIZE/Byte.SIZE;
int bufferSizeInBytes = (int)(file.length()/shortSizeInBytes);

bufferSizeInBytes应该简单地说file.length()fileLength/2是样本数。

在您定义short[] audioData要使用的样本数的地方:

short[] audioData = new short[file.length()/2];

在你构造的地方,AudioTrack你会想要使用字节数:

AudioTrack audioTrack = new AudioTrack(
                AudioManager.STREAM_MUSIC,
                sampleFreq1,
                AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT,
                file.length(),
                AudioTrack.MODE_STREAM);

最后,在您打电话的地方,audioTrack.write您需要再次使用样本数量。

编辑:从您添加到问题的堆栈跟踪中,很明显 AudioTrack 构造函数因错误而失败。audioTrack.status您可以通过在构造函数调用之后调用来检查此错误。我能想到几个可能的问题。

首先,您指定AudioTrack.MODE_STREAM这通常意味着您将为音轨指定一个较小的(可能是几个 kBytes)缓冲区,然后连续调用该write函数以播放整个源缓冲区。AudioTrack.MODE_STATIC另一方面,用于一次播放整个缓冲区 - 正如您尝试做的那样。鉴于目标MODE_STREAM是使用尽可能小的缓冲区,可以想象不支持指定 1.64MB(20 秒 * 每个样本 2 个字节 * 44100)的缓冲区大小。所以我要做的第一件事就是将模式更改为 MODE_STATIC。我不太了解 API,无法告诉您在该模式下是否支持这么大的缓冲区大小。如果没有,那么您将不得不以正确的方式流式传输。

于 2015-09-21T03:30:18.083 回答
1

您在这里只读取一次数据块。

        while(dataInputStream.available() > 0){
            audioData[i] = dataInputStream.readShort();
            i++;
        }

由于可用它不是剩余大小,而是可以在没有阻塞的情况下读取的大小(即它可能为零,而更多数据未读取),您需要读取直到read方法返回小于零(即 EOF)。例如:

// Do here AudioTrack initialization
// ...
InputStream inputStream = new FileInputStream(file);
byte[] byteBuf = new byte[32768]; // just any even size
short[] shortBuf = new short[byteBuf.length / 2]; // buffer of shorts
audioTrack.play(); // start playback. Will wait until first data is written
for(;;) {
    int l = inputStream.read(byteBuf);
    if (l < 0) break; // finish reading when EOF is truly reached 
    int o = 0;
    for (int i = 0; i < l; i += 2) {
        shortBuf[o] = (short)((byteBuf[i] << 8) | (byteBuf[i + 1] & 0xFF)); // convert two big-endian bytes to one short
        o++;
    }
    int res = audioTrack.write(shortBuf, 0, o); // write next portion
    if (res < 0) { 
         // handle audio writing error;
         break;
    }
}
// ...
于 2015-09-21T21:54:27.353 回答
1

Logcat 输出

09-22 01:23:42.940: I/Reverb(22286):  getpid() 22286, IPCThreadState::self()->getCallingPid() 22286
09-22 01:23:42.955: E/AudioTrack(22286): AudioFlinger could not create track, status: -12
09-22 01:23:42.955: E/AudioTrack-JNI(22286): Error initializing AudioTrack
09-22 01:23:42.965: E/AudioTrack-Java(22286): [ android.media.AudioTrack ] Error code -20 when initializing AudioTrack.
09-22 01:23:42.965: D/AndroidRuntime(22286): Shutting down VM
09-22 01:23:42.965: W/dalvikvm(22286): threadid=1: thread exiting with uncaught exception (group=0x40eed2a0)
09-22 01:23:42.985: E/AndroidRuntime(22286): FATAL EXCEPTION: main
09-22 01:23:42.985: E/AndroidRuntime(22286): java.lang.IllegalStateException: play() called on uninitialized AudioTrack.
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.media.AudioTrack.play(AudioTrack.java:887)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at com.example.android2.MainActivity.playRecord(MainActivity.java:225)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at com.example.android2.MainActivity$3.onClick(MainActivity.java:99)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.view.View.performClick(View.java:4211)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.view.View$PerformClick.run(View.java:17267)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.os.Handler.handleCallback(Handler.java:615)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.os.Handler.dispatchMessage(Handler.java:92)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.os.Looper.loop(Looper.java:137)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at android.app.ActivityThread.main(ActivityThread.java:4898)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at java.lang.reflect.Method.invokeNative(Native Method)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at java.lang.reflect.Method.invoke(Method.java:511)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
09-22 01:23:42.985: E/AndroidRuntime(22286):    at dalvik.system.NativeStart.main(Native Method)

下面是修改后的代码

void playRecord(){

        File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

        short[] audioData = new short[(int) (file.length()/2)];
        try {
            InputStream inputStream = new FileInputStream(file);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

            int i = 0;
            while(dataInputStream.available() > 0){
                audioData[i] = dataInputStream.readShort();
                i++;
            }

            dataInputStream.close();

            int selectedPos = spFrequency.getSelectedItemPosition();
            int sampleFreq = freqset[selectedPos];
            int sampleFreq1 = 44100;
            int sampleFreq2 = 44400;
            final String promptPlayRecord = 
                    "PlayRecord()\n"
                    + file.getAbsolutePath() + "\n"
                    + (String)spFrequency.getSelectedItem();

            Toast.makeText(MainActivity.this, 
                    promptPlayRecord, 
                    Toast.LENGTH_LONG).show();

            AudioTrack audioTrack = new AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    sampleFreq1,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    (int)file.length(),
                    AudioTrack.MODE_STREAM);

            int stereo=audioTrack.setStereoVolume(0.0f, 1.0f);

            audioTrack.write(audioData, 0, (int)(file.length()/2));
            audioTrack.play();

            AudioTrack audioTrack2 = new AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    sampleFreq2,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    (int)file.length(),
                    AudioTrack.MODE_STREAM);

            int stereo2=audioTrack2.setStereoVolume(1.0f, 0.0f);

            audioTrack2.write(audioData, 0, (int)(file.length()/2));
            audioTrack2.play();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
于 2015-09-21T17:31:59.183 回答