【问题标题】:AudioTrack - AudioTack not playing fully recorded audioAudioTrack - AudioTack 没有播放完全录制的音频
【发布时间】:2015-12-17 10:17:43
【问题描述】:

我有一个问题,当我录制音频时,我点击了“播放”按钮,它只播放了大约 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();
        }
    }
}

谢谢大家!

【问题讨论】:

    标签: android audio pcm audiotrack channels


    【解决方案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;
        }
    }
    // ...
    

    【讨论】:

      【解决方案2】:

      首先,我假设录制的 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,无法告诉您在该模式下是否支持这么大的缓冲区大小。如果没有,那么您将不得不以正确的方式流式传输。

      【讨论】:

      • 您好,感谢您的帮助
      • 您好,感谢您的帮助。正如你提到的,我已经编辑了代码。但是,如果我尝试用大约 20 秒的时间录制音频并单击“播放”按钮,它只会使应用程序崩溃。为什么会这样?是因为我的缓冲区大小不够吗?您好,感谢您的协助。正如你提到的,我已经编辑了代码。但是,如果我尝试用大约 20 秒的时间录制音频并单击“播放”按钮,它只会使应用程序崩溃。为什么会这样? audioTrack.write(audioData, 0, (int)(file.length()/2)); audioTrack.play();
      • “只是崩溃”相当模糊。抛出了什么异常,从哪一行抛出?
      • 嗨,我在帖子中添加了有关错误的更多详细信息。非常感谢您的建议!谢谢!
      【解决方案3】:

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

      【讨论】:

      • 您正试图在同一输出设备上同时创建两个 AudioTrack。
      猜你喜欢
      • 2014-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-24
      • 1970-01-01
      • 2020-12-20
      相关资源
      最近更新 更多