录制的音频使用AndroidRecord API无法播放

时间:2014-09-22 09:14:01

标签: android android-sdcard pcm android-audiorecord

正在开发一款具有记录用户语音功能的Android应用程序。为此,我使用了AndroidRecord Audio API。

目前,在SD卡中成功生成了pcm文件(录制的音频文件 - recordedAudio.pcm)。但是我无法播放该文件。我也尝试使用Windows媒体播放器和其他一些播放器。但没有任何帮助。

以下是我的代码段

private int minBufSize;
private AudioRecord recorder;
private int sampleRate = 44100;
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private boolean status;

minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
            audioFormat);
status = true;
startStreaming();

public void startStreaming() {
Thread streamThread = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            String filePath = Environment.getExternalStorageDirectory()
                    .getPath() + "/audioRecord.pcm";
            FileOutputStream fileOutputStreamObj = null;
            try {
                fileOutputStreamObj = new FileOutputStream(filePath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Log.e(TAG, "Exception" + e.getMessage());
            }

                // short[] sData = new short[minBufSize];
                byte[] buffer = new byte[minBufSize];

                // recorder = findAudioRecord();
                recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
                        sampleRate, channelConfig, audioFormat, minBufSize);
                Log.d(TAG, "Recorder initialized");

                recorder.startRecording();

                while (status) {
                    // reading data from MIC into buffer
                    minBufSize = recorder.read(buffer, 0, buffer.length);
                    try {
                        // writes the data to file from buffer
                        // stores the voice buffer
                        // byte bData[] = short2byte(sData);

                        fileOutputStreamObj.write(buffer, 0, buffer.length);
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e(TAG, "Exception" + e.getMessage());
                    }
                    // mConnection.sendBinaryMessage(buffer);
                    System.out.println("MinBufferSize: " + minBufSize);
                }

            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "Exception" + e.getMessage());
            }
        }

    });
    streamThread.start();
}

请帮我解决这个问题。提前致谢。

2 个答案:

答案 0 :(得分:2)

您不必将其转换为WAV和Play AudioTrack可以直接播放录制的音频。

以下是使用AudioRecord将音频录制到文件中的代码段,并使用AudioTrack API播放相同内容。

使用按钮从用户控制操作。


代码

private int BufferSize;
byte[] buffer = new byte[BufferSize];

/* AudioRecord and AudioTrack Object */
private AudioRecord record = null;
private AudioTrack track = null;

/* Audio Configuration */
private int sampleRate = 44100;
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

private boolean isRecording = true;
private Thread recordingThread = null;

音频配置可以根据设备进行更改 请参阅this问题。


GUI有三个按钮,记录停止播放

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

    setButtonHandlers();

    /* Set Button Visibility */
    enableButton(R.id.btnStartRec,true);
    enableButton(R.id.btnStopRec,false);
    enableButton(R.id.btnStartPlay,false);

    BufferSize = AudioRecord.getMinBufferSize(sampleRate, 
                       channelConfig, audioFormat); 
}

/* Function to Enable/Disable Buttons */
private void enableButton(int id,boolean isEnable){
    ((Button)findViewById(id)).setEnabled(isEnable);
}

/* Assign OnClickListener to Buttons */
private void setButtonHandlers() {
    ((Button)findViewById(R.id.btnStartRec)).setOnClickListener(btnClick);
    ((Button)findViewById(R.id.btnStopRec)).setOnClickListener(btnClick);
    ((Button)findViewById(R.id.btnStartPlay)).setOnClickListener(btnClick);
}

处理按钮点击:

private View.OnClickListener btnClick = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        switch(v.getId()){
        case R.id.btnStartRec:{
            Log.d(TAG, "Start Recording");
            enableButton(R.id.btnStartRec,false);
            enableButton(R.id.btnStopRec,true);
            startRecording();
            break;
        }
        case R.id.btnStopRec:{
            Log.d(TAG, "Stop Recording");
            enableButton(R.id.btnStartRec,true);
            enableButton(R.id.btnStopRec,false);
            stopRecording();
            enableButton(R.id.btnStartPlay,true);
            break;
        }
        case R.id.btnStartPlay:{
            Log.d(TAG, "Play Recording");
            enableButton(R.id.btnStartRec,false);
            enableButton(R.id.btnStopRec,false);
            StartPlaying();
            break;
        }
        }
    }
};

开始录制代码

private void startRecording()
{
    record = new AudioRecord(AudioSource.DEFAULT, sampleRate, 
                                channelConfig, audioFormat, BufferSize);
    if (AudioRecord.STATE_INITIALIZED == record.getState())
        record.startRecording();

    isRecording = true;

    /* Run a thread for Recording */
    recordingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            writeAudioDataToFile();
        }
    },"AudioRecorder Thread");
    recordingThread.start();
}


private void writeAudioDataToFile()
{
    byte data[] = new byte[BufferSize];

    /* Record audio to following file */
    String filename = "/sdcard/audiofile.pcm";
    FileOutputStream os = null;

    try {
        os = new FileOutputStream(filename);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    int read_bytes = 0;

    if(null != os){
        while(isRecording)
        {
            read_bytes = record.read(data, 0, BufferSize);

            if(AudioRecord.ERROR_INVALID_OPERATION != read_bytes){
                try {
                    os.write(data);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

停止录制代码

private void stopRecording()
{
    if(null != record)
    {
        isRecording = false;

        if (AudioRecord.STATE_INITIALIZED == record.getState()) 
        {
            record.stop();
            record.release();
            Log.d(TAG, "===== Recording Audio Completed ===== ");
        }

        record = null;
        recordingThread = null;
    }
}   

播放音频文件的代码:

public void startPlaying()
{
    enableButton(R.id.btnStartPlay,false);

    int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, 
            AudioFormat.CHANNEL_OUT_MONO, 
            AudioFormat.ENCODING_PCM_16BIT);

    track = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, 
                            AudioFormat.CHANNEL_OUT_MONO, 
                            AudioFormat.ENCODING_PCM_16BIT, minBufferSize, 
                            AudioTrack.MODE_STREAM);

    int i = 0;
    byte[] temp = new byte[minBufferSize];

    try {
        FileInputStream fin = new FileInputStream("/sdcard/audiofile.pcm");
        Log.d(TAG, "===== Opening File for Playing : /sdcard/audiofile.pcm ===== ");

        DataInputStream dis = new DataInputStream(fin); 

        track.play();
        while((i = dis.read(temp, 0, minBufferSize)) > -1)
        {
            track.write(temp, 0, i);
        }

        Log.d(TAG, "===== Playing Audio Completed ===== ");
        track.stop();
        track.release();
        dis.close();
        fin.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    enableButton(R.id.btnStartRec,true);
}

请在AndroidManifest.xml

中加入以下内容
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" > </uses-permission>
<uses-permission android:name="android.permission.RECORD_AUDIO" >  </uses-permission>

activity_main.xml看起来像this string.xml看起来像this

以上代码正在运行并经过测试。

您也可以这样做,不用文件并使用中间缓冲区
请参阅:Audio Recording and Streaming in Android

答案 1 :(得分:1)

是的,我终于找到了迈克尔上述评论线索的答案。

我在这里张贴了工作代码。

客户端代码,如下所示, 从客户端将音频数据流式传输到Web套接字服务器。

private int minBufSize;
private AudioRecord recorder;
private int sampleRate = 44100;
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
                audioFormat);
startStreaming();

public void startStreaming() {
        Thread streamThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {

                    byte[] buffer = new byte[minBufSize];

                    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
                            sampleRate, channelConfig, audioFormat, minBufSize);
                    Log.d(TAG, "Recorder initialized");

                    recorder.startRecording();

                    while (status) {

                        // reading data from MIC into buffer
                        minBufSize = recorder.read(buffer, 0, buffer.length);

                        mConnection.sendBinaryMessage(buffer);
                        System.out.println("MinBufferSize: " + minBufSize);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    Log.e(TAG, "Exception" + e.getMessage());
                }
            }

        });
        streamThread.start();
    }

服务器端代码添加了如下实现, 首先,服务器将从流数据创建.pcm。然后从该pcm文件中,它将通过添加标题来创建wave文件。

  @OnMessage
  public void onMessage(byte[] data, boolean arg1)
  {
    if ((!this.currentCommand.equals("stop")) && 
      (this.currentCommand.equals("start")))
      try {
        System.out.println("Starting new recording.");
        FileOutputStream fOut = new FileOutputStream(this.f2, true);
        fOut.write(data);
        fOut.close();

        properWAV(this.f2, 111133.0F);
      }
      catch (Exception e) {
        e.printStackTrace();
      }
  }

private void properWAV(File fileToConvert, float newRecordingID)
  {
    try {
      long mySubChunk1Size = 16L;
      int myBitsPerSample = 16;
      int myFormat = 1;
      long myChannels = 1L;
      long mySampleRate = 44100L;
      long myByteRate = mySampleRate * myChannels * myBitsPerSample / 8L;
      int myBlockAlign = (int)(myChannels * myBitsPerSample / 8L);

      byte[] clipData = getBytesFromFile(fileToConvert);

      long myDataSize = clipData.length;
      long myChunk2Size = myDataSize * myChannels * myBitsPerSample / 8L;
      long myChunkSize = 36L + myChunk2Size;

      OutputStream os = new FileOutputStream(new File("D:/audio/" + newRecordingID + ".wav"));
      BufferedOutputStream bos = new BufferedOutputStream(os);
      DataOutputStream outFile = new DataOutputStream(bos);

      outFile.writeBytes("RIFF");
      outFile.write(intToByteArray((int)myChunkSize), 0, 4);
      outFile.writeBytes("WAVE");
      outFile.writeBytes("fmt ");
      outFile.write(intToByteArray((int)mySubChunk1Size), 0, 4);
      outFile.write(shortToByteArray((short)myFormat), 0, 2);
      outFile.write(shortToByteArray((short)(int)myChannels), 0, 2);
      outFile.write(intToByteArray((int)mySampleRate), 0, 4);
      outFile.write(intToByteArray((int)myByteRate), 0, 4);
      outFile.write(shortToByteArray((short)myBlockAlign), 0, 2);
      outFile.write(shortToByteArray((short)myBitsPerSample), 0, 2);
      outFile.writeBytes("data");
      outFile.write(intToByteArray((int)myDataSize), 0, 4);
      outFile.write(clipData);

      outFile.flush();
      outFile.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }

  private static byte[] intToByteArray(int i)
  {
    byte[] b = new byte[4];
    b[0] = (byte)(i & 0xFF);
    b[1] = (byte)(i >> 8 & 0xFF);
    b[2] = (byte)(i >> 16 & 0xFF);
    b[3] = (byte)(i >> 24 & 0xFF);
    return b;
  }

  public static byte[] shortToByteArray(short data)
  {
    return new byte[] { (byte)(data & 0xFF), (byte)(data >>> 8 & 0xFF) };
  }

  public byte[] getBytesFromFile(File file)
    throws IOException
  {
    byte[] buffer = new byte[(int)file.length()];
    InputStream ios = null;
    try {
      ios = new FileInputStream(file);
      if (ios.read(buffer) == -1)
        throw new IOException("EOF reached while trying to read the whole file");
    }
    finally {
      try {
        if (ios != null)
          ios.close();
      }
      catch (IOException localIOException)
      {
      }
    }
    try
    {
      if (ios != null)
        ios.close();
    }
    catch (IOException localIOException1)
    {
    }
    return buffer;
  }

希望这个能节省很多开发人员的时间。