如何播放 pcm 文件

Posted

技术标签:

【中文标题】如何播放 pcm 文件【英文标题】:how can i play pcm file 【发布时间】:2013-06-09 16:33:03 【问题描述】:

以下代码应录制音频并将其以 PCM 格式存储到 SD 卡中。 代码正在与我一起工作,但 PCM 文件无法播放!!!!

我从这个链接得到了这个代码......android : recording audio using audiorecord class play as fast forwarded

我需要播放 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 你如何测试它是否播放? 我去了存储它的路径,当我试图打开它时,它说:“无法找到执行此操作的应用程序”@Ken Wolf 【参考方案1】:

默认情况下,Android 的媒体播放器不播放 PCM 文件。要么

    将其从 SD 卡复制到计算机并在那里播放 使用AudioTrack编写您自己的播放器 安装播放 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。

【讨论】:

我把它复制到我的电脑上,但它没有工作它说“无法渲染这个文件” 很难从这里诊断,但尝试更改为 .wav 并在 windows 媒体播放器中打开(假设您使用的是 windows) 我下载了Audacity程序,它打开了PCM文件,但不是我录制的声音,它像快进一样,像吱吱作响的声音!不知道是什么问题?! @肯狼 听起来播放的采样率与录制的不同 非常感谢,你是对的,原来我使用的采样率与@Ken Wolf 录制的采样率不同【参考方案2】:

我也使用了您的代码,但我的语音记录就像“zzzzz”记录。所以我修改了一点代码,现在我可以通过智能手机和 PC(在本例中使用 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”返回的缓冲区大小。 如何在PlayShortAudioFileViaAudioTrack 函数中使用byteData 访问原始声音数据(可能是整数)?当我尝试打印 byteData 时,我得到了全零,但是当我使用 sublime 编辑器打开 .pcm 文件时,我看到了十六进制值。【参考方案3】:

这是我的解决方案

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

    

【讨论】:

我为您改进了代码缩进。您可以通过稍微解释一下您的代码(包括为什么以及如何解决问题的问题)来进一步改进您的答案。 16000 采样率对于 pcm 音频来说太慢了吧??【参考方案4】:

私人无效 startRecording() if(bufferSize == AudioRecord.ERROR_BAD_VALUE) Log.e( TAG, "Bad Value for \"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();

替换录音代码...

【讨论】:

以上是关于如何播放 pcm 文件的主要内容,如果未能解决你的问题,请参考以下文章

如何正确播放已解码的内存PCM与Oboe?

如何将 PCM 音频流转换为在线播放

在Swift iOS中播放PCM文件

在 Swift iOS 中播放 PCM 文件

无法在 Android 中播放使用 AudioRecord 录制的 pcm 文件

如何同步2个音频文件?另一个的录音版本在哪里?