我能够在我的应用程序中录制和播放音频,但在导出时我无法重现音频.我找到的唯一方法是导出我的.pcm文件并使用Audacity进行转换.
这是我记录音频的代码是:
private Thread recordingThread private AudioRecord mRecorder; private boolean isRecording = false; private void startRecording() { mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC,Constants.RECORDER_SAMPLERATE,Constants.RECORDER_CHANNELS,Constants.RECORDER_AUdio_ENCODING,Constants.BufferElements2Rec * Constants.BytesPerElement); mRecorder.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 FileOutputStream os = null; try { os = new FileOutputStream(mFileName); } catch (FileNotFoundException e) { e.printStackTrace(); } while (isRecording) { // gets the voice output from microphone to byte format mRecorder.read(sData,Constants.BufferElements2Rec); try { // // writes the data to file from buffer // // stores the voice buffer byte bData[] = short2byte(sData); os.write(bData,Constants.BufferElements2Rec * Constants.BytesPerElement); } catch (IOException e) { e.printStackTrace(); } } try { os.close(); } catch (IOException e) { e.printStackTrace(); } }
要播放录制的音频,代码为:
private void startPlaying() { new Thread(new Runnable() { public void run() { try { File file = new File(mFileName); byte[] audioData = null; InputStream inputStream = new FileInputStream(mFileName); audioData = new byte[Constants.BufferElements2Rec]; mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC,AudioFormat.CHANNEL_OUT_MONO,Constants.BufferElements2Rec * Constants.BytesPerElement,AudioTrack.MODE_STREAM); final float duration = (float) file.length() / Constants.RECORDER_SAMPLERATE / 2; Log.i(TAG,"PLAYBACK AUdio"); Log.i(TAG,String.valueOf(duration)); mPlayer.setPositionNotificationPeriod(Constants.RECORDER_SAMPLERATE / 10); mPlayer.setNotificationMarkerPosition(Math.round(duration * Constants.RECORDER_SAMPLERATE)); mPlayer.play(); int i = 0; while ((i = inputStream.read(audioData)) != -1) { try { mPlayer.write(audioData,i); } catch (Exception e) { Log.e(TAG,"Exception: " + e.getLocalizedMessage()); } } } catch (FileNotFoundException fe) { Log.e(TAG,"File not found: " + fe.getLocalizedMessage()); } catch (IOException io) { Log.e(TAG,"IO Exception: " + io.getLocalizedMessage()); } } }).start(); }
Constants类中定义的常量是:
public class Constants { final static public int RECORDER_SAMPLERATE = 44100; final static public int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO; final static public int RECORDER_AUdio_ENCODING = AudioFormat.ENCODING_PCM_16BIT; final static public int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024 final static public int BytesPerElement = 2; // 2 bytes in 16bit format }
如果我按原样导出文件,我将其转换为Audacity并播放.但是,我需要以可以自动播放的格式导出它.
我已经看到了实施Lame的答案,目前正在研究它.我也找到了使用以下方法转换它的答案:
private File rawToWave(final File rawFile,final String filePath) throws IOException { File waveFile = new File(filePath); byte[] rawData = new byte[(int) rawFile.length()]; DataInputStream input = null; try { input = new DataInputStream(new FileInputStream(rawFile)); input.read(rawData); } finally { if (input != null) { input.close(); } } DataOutputStream output = null; try { output = new DataOutputStream(new FileOutputStream(waveFile)); // WAVE header // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ writeString(output,"RIFF"); // chunk id writeInt(output,36 + rawData.length); // chunk size writeString(output,"WAVE"); // format writeString(output,"fmt "); // subchunk 1 id writeInt(output,16); // subchunk 1 size writeShort(output,(short) 1); // audio format (1 = PCM) writeShort(output,(short) 1); // number of channels writeInt(output,Constants.RECORDER_SAMPLERATE); // sample rate writeInt(output,Constants.RECORDER_SAMPLERATE * 2); // byte rate writeShort(output,(short) 2); // block align writeShort(output,(short) 16); // bits per sample writeString(output,"data"); // subchunk 2 id writeInt(output,rawData.length); // subchunk 2 size // Audio data (conversion big endian -> little endian) short[] shorts = new short[rawData.length / 2]; ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts); ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2); for (short s : shorts) { bytes.putShort(s); } output.write(bytes.array()); } finally { if (output != null) { output.close(); } } return waveFile; } private void writeInt(final DataOutputStream output,final int value) throws IOException { output.write(value >> 0); output.write(value >> 8); output.write(value >> 16); output.write(value >> 24); } private void writeShort(final DataOutputStream output,final short value) throws IOException { output.write(value >> 0); output.write(value >> 8); } private void writeString(final DataOutputStream output,final String value) throws IOException { for (int i = 0; i < value.length(); i++) { output.write(value.charAt(i)); } }
但是,这在导出时会播放正确的持续时间,但只是白噪声.
我尝试但无法工作的一些答案:
> Android:Creating Wave file using Raw PCM,the wave file does not play
> How to convert PCM raw data to mp3 file?
> converting pcm file to mp3 using liblame in android
任何人都可以指出什么是最好的解决方案?它是真的实施跛脚还是可以更直接的方式完成?如果是这样,为什么代码示例将文件转换为白噪声?
解决方法
private void rawToWave(final File rawFile,final File waveFile) throws IOException { byte[] rawData = new byte[(int) rawFile.length()]; DataInputStream input = null; try { input = new DataInputStream(new FileInputStream(rawFile)); input.read(rawData); } finally { if (input != null) { input.close(); } } DataOutputStream output = null; try { output = new DataOutputStream(new FileOutputStream(waveFile)); // WAVE header // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ writeString(output,44100); // sample rate writeInt(output,RECORDER_SAMPLERATE * 2); // byte rate writeShort(output,rawData.length); // subchunk 2 size // Audio data (conversion big endian -> little endian) short[] shorts = new short[rawData.length / 2]; ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts); ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2); for (short s : shorts) { bytes.putShort(s); } output.write(fullyReadFileToBytes(rawFile)); } finally { if (output != null) { output.close(); } } } byte[] fullyReadFileToBytes(File f) throws IOException { int size = (int) f.length(); byte bytes[] = new byte[size]; byte tmpBuff[] = new byte[size]; FileInputStream fis= new FileInputStream(f); try { int read = fis.read(bytes,size); if (read < size) { int remain = size - read; while (remain > 0) { read = fis.read(tmpBuff,remain); System.arraycopy(tmpBuff,bytes,size - remain,read); remain -= read; } } } catch (IOException e){ throw e; } finally { fis.close(); } return bytes; } private void writeInt(final DataOutputStream output,final String value) throws IOException { for (int i = 0; i < value.length(); i++) { output.write(value.charAt(i)); } }
如何使用
它使用起来非常简单.只需将其称为:
File f1 = new File("/sdcard/44100Sampling-16bit-mono-mic.pcm"); // The location of your PCM file File f2 = new File("/sdcard/44100Sampling-16bit-mono-mic.wav"); // The location where you want your WAV file try { rawToWave(f1,f2); } catch (IOException e) { e.printStackTrace(); }
这一切是如何运作的
如您所见,WAV标头是WAV和PCM文件格式之间的唯一区别.假设您正在录制16位PCM MONO音频(根据您的代码,您是). rawToWave函数只是巧妙地将标题添加到WAV文件中,以便音乐播放器知道打开文件时会发生什么,然后在标题之后,它只会从最后一位开始写入PCM数据.
酷提示
如果你想改变你的声音音调,或者做一个语音转换器应用程序,你所要做的就是增加/减少writeInt的值(输出,44100); //代码中的采样率减小它将告诉玩家以不同的速率播放它,从而改变输出音高.只是一点额外的’好知道’的事情.