问题
I recorded an audio using the audio recorder on Android and it produces a raw PCM file. I'm trying to convert it to a format I can listen to (wav or mp3 for example.
I've started with this example but don't know where to go from here: Android AudioRecord example
tried following these: http://computermusicblog.com/blog/2008/08/29/reading-and-writing-wav-files-in-java
Recording .Wav with Android AudioRecorder
Here is my code to record (note I am using Countdown Timer to tell it when to start and stop recording.
public class AudioRecordService extends Service {
Toast toast;
private static final int RECORDER_SAMPLERATE = 44100;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private AudioRecord record = null;
int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
int BytesPerElement = 2; // 2 bytes in 16bit format
private Thread recordingThread = null;
private boolean isRecording = false;
int buffsize = 0;
public AudioRecordService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
public int onStartCommand(Intent intent, int flags, int startId)
{
try {
buffsize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
record = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLERATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING, buffsize);
record.startRecording();
CountDownTimer countDowntimer = new CountDownTimer(15000, 1000) {
public void onTick(long millisUntilFinished) {
toast = Toast.makeText(AudioRecordService.this, "Recording", Toast.LENGTH_SHORT);
toast.show();
isRecording = true;
recordingThread = new Thread(new Runnable() {
public void run() {
writeAudioDataToFile();
}
}, "AudioRecorder Thread");
recordingThread.start();
}
public void onFinish() {
try {
toast.cancel();
Toast.makeText(AudioRecordService.this, "Done Recording ", Toast.LENGTH_SHORT).show();
isRecording = false;
record.stop();
record.release();
record = null;
recordingThread = null;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}};
countDowntimer.start();
}
catch (Exception ex)
{
ex.printStackTrace();
}
return Service.START_STICKY;
}
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() {
try {
//String filePath = "/sdcard/voice8K16bitmono.pcm";
String extState = Environment.getExternalStorageState();
// Path to write files to
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC + "/test").getAbsolutePath();
String fileName = "audio.pcm";
String externalStorage = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(externalStorage + File.separator + fileName);
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
short sData[] = new short[BufferElements2Rec];
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
while (isRecording) {
// gets the voice output from microphone to byte format
record.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();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
My audio.pcm is created. However I don't know how to play it. I'm assuming bDate[] is the byte array being written. The links I created said they used the files, but showed no examples of how it was done.
If it matters, I've used GoldWave to open the file. It opens but the audio is messed up.
I also noticed my files were 2 seconds and I think it's because of the BytesPerElement and BufferElements2Rec. If you can help me out so it's going to be 15 seconds that would be great.
Thanks in advance!
回答1:
The only difference between a PCM file and a WAV file is that the PCM file has no header and the WAV file does. The WAV header has key information for playback such as sample rate, number of bits per sample and number of channels. When you load a PCM file either the app has to have prior knowledge of this information or you have to tell it. If you load a PCM file into audacity, for example, it will prompt you to fill in all of that stuff.
In order to make the existing save file a .WAV you need to prepend an appropriate header. I'm not going to go into details about it because there are already many answers on SO detailing it and it is readily available on the web (https://en.wikipedia.org/wiki/WAV)
The second issue you raise about the file length might have something to do with the fact that AudioRecord.read
returns an int which is the number of samples actually read as it may be less than you asked for. This is really a second question though
回答2:
This is a Sample .WAV
Header format Extracted from OMRECORDER:
private byte[] wavFileHeader(long totalAudioLen, long totalDataLen, long longSampleRate,
int channels, long byteRate, byte bitsPerSample) {
byte[] header = new byte[44];
header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (totalDataLen & 0xff);
header[5] = (byte) ((totalDataLen >> 8) & 0xff);
header[6] = (byte) ((totalDataLen >> 16) & 0xff);
header[7] = (byte) ((totalDataLen >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f'; // 'fmt ' chunk
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1; // format = 1
header[21] = 0;
header[22] = (byte) channels;
header[23] = 0;
header[24] = (byte) (longSampleRate & 0xff);
header[25] = (byte) ((longSampleRate >> 8) & 0xff);
header[26] = (byte) ((longSampleRate >> 16) & 0xff);
header[27] = (byte) ((longSampleRate >> 24) & 0xff);
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
header[32] = (byte) (channels * (bitsPerSample / 8)); //
// block align
header[33] = 0;
header[34] = bitsPerSample; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
return header;
}
Below is Header format for .aac
extracted from WhatsappAudioRecorder:
private byte[] createAdtsHeader(int length) {
int frameLength = length + 7;
byte[] adtsHeader = new byte[7];
adtsHeader[0] = (byte) 0xFF; // Sync Word
adtsHeader[1] = (byte) 0xF1; // MPEG-4, Layer (0), No CRC
adtsHeader[2] = (byte) ((MediaCodecInfo.CodecProfileLevel.AACObjectLC - 1) << 6);
adtsHeader[2] |= (((byte) SAMPLE_RATE_INDEX) << 2);
adtsHeader[2] |= (((byte) CHANNELS) >> 2);
adtsHeader[3] = (byte) (((CHANNELS & 3) << 6) | ((frameLength >> 11) & 0x03));
adtsHeader[4] = (byte) ((frameLength >> 3) & 0xFF);
adtsHeader[5] = (byte) (((frameLength & 0x07) << 5) | 0x1f);
adtsHeader[6] = (byte) 0xFC;
return adtsHeader;
}
来源:https://stackoverflow.com/questions/32218360/android-audio-record-to-wav