如何播放PCM文件

7
以下代码应记录音频并以PCM格式存储到SD卡中。 代码在我的情况下可以工作,但PCM文件无法播放 !!!!
我从这个链接获得了这个代码.... Android:使用audiorecord类记录音频快速播放 我需要播放PCM文件,我该怎么做?
public class Audio_Record extends Activity {
private static final int RECORDER_SAMPLERATE = 8000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private AudioRecord recorder = null;
private Thread recordingThread = null;
private boolean isRecording = false;

 @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

setButtonHandlers();
enableButtons(false);

int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
        RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);

System.out.println("BUFFER SIZE VALUE IS " + bufferSize);

 }

       private void setButtonHandlers() {
 ((Button) findViewById(R.id.btnStart)).setOnClickListener(btnClick);
((Button) findViewById(R.id.btnStop)).setOnClickListener(btnClick);
    }

   private void enableButton(int id, boolean isEnable) {
((Button) findViewById(id)).setEnabled(isEnable);
    }

private void enableButtons(boolean isRecording) {
enableButton(R.id.btnStart, !isRecording);
enableButton(R.id.btnStop, isRecording);
   }

  int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we
                            // use only 1024
  int BytesPerElement = 2; // 2 bytes in 16bit format

   private void startRecording() {

recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
        RECORDER_SAMPLERATE, RECORDER_CHANNELS,
        RECORDER_AUDIO_ENCODING, BufferElements2Rec * BytesPerElement);

recorder.startRecording();
isRecording = true;

recordingThread = new Thread(new Runnable() {

      public void run() {

        writeAudioDataToFile();

    }
}, "AudioRecorder Thread");
recordingThread.start();
    }

private byte[] short2byte(short[] sData) {
int shortArrsize = sData.length;
byte[] bytes = new byte[shortArrsize * 2];

for (int i = 0; i < shortArrsize; i++) {
    bytes[i * 2] = (byte) (sData[i] & 0x00FF);
    bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
    sData[i] = 0;
}
return bytes;

  }

  private void writeAudioDataToFile() {
// Write the output audio in byte

String filePath = "/sdcard/voice8K16bitmono.pcm";
short sData[] = new short[BufferElements2Rec];

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

while (isRecording) {
    // gets the voice output from microphone to byte format

    recorder.read(sData, 0, BufferElements2Rec);
    System.out.println("Short wirting to file" + sData.toString());
    try {
        // // writes the data to file from buffer
        // // stores the voice buffer

        byte bData[] = short2byte(sData);

        os.write(bData, 0, BufferElements2Rec * BytesPerElement);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

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

private void stopRecording() {
// stops the recording activity
if (null != recorder) {
    isRecording = false;

    recorder.stop();
    recorder.release();

    recorder = null;
    recordingThread = null;
}
 }

private View.OnClickListener btnClick = new View.OnClickListener() {
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.btnStart: {
        enableButtons(true);
        startRecording();
        break;
    }
    case R.id.btnStop: {
        enableButtons(false);
        stopRecording();
        break;
    }
    }
}
 };

  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK) {

    finish();
}
return super.onKeyDown(keyCode, event);
 }
}

请查看我的问题@TechEnd。 - Haneen Bassam
你是如何测试它是否能够播放的? - Ken Wolf
我去了存储它的路径,当我尝试打开它时,它显示:“无法找到执行此操作的应用程序”@Ken Wolf - Haneen Bassam
4个回答

4

我也使用了你的代码,但我的录音听起来像是“嗡嗡”的声音。所以我稍微修改了代码,现在我可以在智能手机和电脑上(在这种情况下使用Audacity)无问题地聆听录音。

这是我的代码:

public class VoiceActivity extends Activity {
private static final String TAG = "VoiceRecord";

private static final int RECORDER_SAMPLERATE = 8000;     
private static final int RECORDER_CHANNELS_IN = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_CHANNELS_OUT = AudioFormat.CHANNEL_OUT_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

private static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;

// Initialize minimum buffer size in bytes.
private int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS_IN, RECORDER_AUDIO_ENCODING);

private AudioRecord recorder = null;
private Thread recordingThread = null;
private boolean isRecording = false;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_voice);

    ((Button) findViewById(R.id.start_button)).setOnClickListener(btnClick);
    ((Button) findViewById(R.id.stop_button)).setOnClickListener(btnClick);
    enableButtons(false);   
}

private void enableButton(int id, boolean isEnable) {
    ((Button) findViewById(id)).setEnabled(isEnable);
}

private void enableButtons(boolean isRecording) {
    enableButton(R.id.start_button, !isRecording);
    enableButton(R.id.stop_button, isRecording);
}

private void startRecording() {
    if( bufferSize == AudioRecord.ERROR_BAD_VALUE)
        Log.e( TAG, "Bad Value for \"bufferSize\", recording parameters are not supported by the hardware");

    if( bufferSize == AudioRecord.ERROR )
        Log.e( TAG, "Bad Value for \"bufferSize\", implementation was unable to query the hardware for its output properties");

    Log.e( TAG, "\"bufferSize\"="+bufferSize);

    // Initialize Audio Recorder.    
    recorder = new AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLERATE, RECORDER_CHANNELS_IN, RECORDER_AUDIO_ENCODING, bufferSize);
    // Starts recording from the AudioRecord instance.
    recorder.startRecording();

    isRecording = true;

    recordingThread = new Thread(new Runnable() {
        public void run() {
            writeAudioDataToFile();
        }
    }, "AudioRecorder Thread");
    recordingThread.start();
}

private void writeAudioDataToFile() {
    //Write the output audio in byte
    String filePath = "/sdcard/8k16bitMono.pcm";
    byte saudioBuffer[] = new byte[bufferSize];

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

    while (isRecording) {
        // gets the voice output from microphone to byte format
        recorder.read(saudioBuffer, 0, bufferSize);
        try {
            //  writes the data to file from buffer stores the voice buffer
            os.write(saudioBuffer, 0, bufferSize);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    try {
        os.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void stopRecording() throws IOException {
    //  stops the recording activity
    if (null != recorder) {
        isRecording = false;  
        recorder.stop();
        recorder.release();
        recorder = null;
        recordingThread = null;
        PlayShortAudioFileViaAudioTrack("/sdcard/8k16bitMono.pcm");
    }   
}

private void PlayShortAudioFileViaAudioTrack(String filePath) throws IOException{
    // We keep temporarily filePath globally as we have only two sample sounds now..
    if (filePath==null)
        return;

    //Reading the file.. 
    File file = new File(filePath); // for ex. path= "/sdcard/samplesound.pcm" or "/sdcard/samplesound.wav"
    byte[] byteData = new byte[(int) file.length()];
    Log.d(TAG, (int) file.length()+"");

    FileInputStream in = null;
    try {
        in = new FileInputStream( file );
        in.read( byteData );
        in.close(); 
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    // Set and push to audio track..
    int intSize = android.media.AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS_OUT, RECORDER_AUDIO_ENCODING); 
    Log.d(TAG, intSize+"");

    AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS_OUT, RECORDER_AUDIO_ENCODING, intSize, AudioTrack.MODE_STREAM); 
    if (at!=null) { 
        at.play();
        // Write the byte array to the track
        at.write(byteData, 0, byteData.length); 
        at.stop();
        at.release();
    }
    else
        Log.d(TAG, "audio track is not initialised ");

}

private View.OnClickListener btnClick = new View.OnClickListener() {
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.start_button: {
            enableButtons(true);
            startRecording();
            break;
        }
        case R.id.stop_button: {
            enableButtons(false);
            try {
                stopRecording();
            } catch (IOException e) {
                //  TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;
        }
        }
    }   
};

    // onClick of backbutton finishes the activity.
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        finish();
    }
    return super.onKeyDown(keyCode, event);
}
}

谢谢你的修改!我也遇到了类似的音频失真问题,你的解决方案解决了这个问题。原始代码截断了录音,所以才会出现失真。必须使用“AudioRecord.getMinBufferSize”返回的缓冲区大小。 - us_david
如何使用PlayShortAudioFileViaAudioTrack函数中的byteData访问原始声音数据(可能为整数)?当我尝试打印byteData时,我得到了所有的零值,但是当我使用Sublime编辑器打开.pcm文件时,我看到十六进制值。 - mio

4

默认情况下,Android的媒体播放器无法播放PCM文件。您可以选择以下方法:

  1. 将文件从SD卡复制到计算机上进行播放。
  2. 使用AudioTrack编写自己的播放器。
  3. 安装支持PCM的应用程序。

这里有一个使用AudioTrack类播放PCM的教程:(http://jongladwin.blogspot.co.uk/2010/03/android-play-pcmwav-audio-buffer-using.html)

Windows Media Player应该能够播放PCM,这里提供了一些替代方案:(http://www.makeuseof.com/answers/play-pcm-file-pc/)

我想大多数Android上的主流音乐播放器应该都支持PCM格式。


我把它复制到了我的电脑上,但是它无法工作,显示“无法渲染此文件”。 - Haneen Bassam
很难从这里诊断,但尝试将文件格式更改为.wav并在Windows Media Player中打开(假设您正在使用Windows)。 - Ken Wolf
我下载了一个叫做Audacity的程序,它打开了PCM文件,但是播放出来的声音不是我录制的声音,而是像快进一样,像尖叫声!我不知道问题出在哪里?@Ken Wolf - Haneen Bassam
听起来回放的采样率与录制的不同。 - Ken Wolf
非常感谢,你说得对,事实证明我使用的采样率与Ken Wolf录制的不同。 - Haneen Bassam

2

这是我的解决方案

public class AudioTrackPlayer {
    private String pathAudio;
    private AudioTrack audioPlayer;
    private Thread mThread;
    private int bytesread = 0, ret = 0;
    private int size;
    private FileInputStream in = null;
    private byte[] byteData = null;
    private int count = 512 * 1024; // 512 kb
    private boolean isPlay = true;
    private boolean isLooping = false;
    private static Handler mHandler;

    public AudioTrackPlayer() {

    }

    public void prepare(String pathAudio){
        this.pathAudio = pathAudio;
        mHandler = new Handler();
    }

    public void play(){
        stop();

        isPlay = true;
        bytesread = 0;
        ret = 0;
        if (pathAudio == null)
            return;

        audioPlayer = createAudioPlayer();
        if (audioPlayer == null) return;
        audioPlayer.play();

        mThread = new Thread(new PlayerProcess());
        mThread.start();
    }

    private final Runnable mLopingRunnable = new Runnable() {
        @Override
        public void run() {
            play();
        }
    };

    private AudioTrack createAudioPlayer(){
        int intSize = android.media.AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 16000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT, intSize, AudioTrack.MODE_STREAM);
        if (audioTrack == null) {
            Log.d("TCAudio", "audio track is not initialised ");
            return null;
        }

        File file = null;
        file = new File(pathAudio);

        byteData = new byte[(int) count];
        try {
            in = new FileInputStream(file);

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

        size = (int) file.length();
        return  audioTrack;
    }

    private class PlayerProcess implements Runnable{

        @Override
        public void run() {
            while (bytesread < size && isPlay) {
                if (Thread.currentThread().isInterrupted()) {
                    break;
                }
                try {
                    ret = in.read(byteData, 0, count);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (ret != -1) { // Write the byte array to the track
                    audioPlayer.write(byteData,0, ret);
                    bytesread += ret;
                } else break;
            }
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (audioPlayer!=null){
                if (audioPlayer.getState()!=AudioTrack.PLAYSTATE_STOPPED){
                    audioPlayer.stop();
                    audioPlayer.release();
                    mThread = null;
                }
            }
            if (isLooping && isPlay ) mHandler.postDelayed(mLopingRunnable,100);
        }
    }

    public void setLooping(){
        isLooping = !isLooping;
    }

    public void pause(){

    }

    public void stop(){
        isPlay = false;
        if (mThread != null) {
            mThread.interrupt();
            mThread = null;
        }
        if (audioPlayer != null) {
            audioPlayer.stop();
            audioPlayer.release();
            audioPlayer = null;
        }
    }

    public void reset(){

    }
}

我为你改进了代码缩进。你可以通过解释你的代码,包括为什么以及如何修复问题来进一步改善你的答案。 - mmgross
16000采样率对于PCM音频来说太慢了,对吧? - hushed_voice

0
私有 void startRecording() { 如果 (bufferSize == AudioRecord.ERROR_BAD_VALUE) Log.e(TAG, "“bufferSize” 的值不正确,录音参数不受硬件支持"); }
if( bufferSize == AudioRecord.ERROR )
    Log.e( TAG, "Bad Value for \"bufferSize\", implementation was unable to query the hardware for its output properties");

Log.e( TAG, "\"bufferSize\"="+bufferSize);

// Initialize Audio Recorder.    
recorder = new AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLERATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, RECORDER_AUDIO_ENCODING, bufferSize);
// Starts recording from the AudioRecord instance.
recorder.startRecording();

isRecording = true;

recordingThread = new Thread(new Runnable() {
    public void run() {
        writeAudioDataToFile();
    }
}, "AudioRecorder Thread");
recordingThread.start();

}

替换录制代码...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接