我使用Android上的录音机录制了一个音频,它生成一个原始PCM文件。我正在尝试将其转换为我可以听的格式(例如wav或mp3。
我从这个例子开始,但不知道从哪里开始:Android AudioRecord example
尝试了以下这些: http://computermusicblog.com/blog/2008/08/29/reading-and-writing-wav-files-in-java
Recording .Wav with Android AudioRecorder
这是我要记录的代码(注意我正在使用倒数计时器告诉它何时开始和停止录制。
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();
}
}
}
我的audio.pcm已创建。但是我不知道怎么玩。我假设bDate []是正在写入的字节数组。我创建的链接表示他们使用了这些文件,但没有显示它是如何完成的示例。
如果重要,我使用GoldWave打开文件。它会打开,但音频搞砸了。
我还注意到我的文件是2秒,我认为这是因为BytesPerElement和BufferElements2Rec。如果你可以帮助我,那么15秒就会很棒。
提前致谢!
答案 0 :(得分:3)
PCM文件和WAV文件之间的唯一区别是PCM文件没有标题而WAV文件没有标题。 WAV标题具有用于重放的关键信息,例如采样率,每个样本的比特数和信道数。当您加载PCM文件时,应用程序必须事先了解此信息,或者您必须告诉它。例如,如果您将PCM文件加载到audacity中,它将提示您填写所有这些内容。
为了使现有的保存文件成为.WAV,您需要预先添加适当的标头。我不打算详细介绍它,因为已经有很多答案详细说明了它,并且很容易在网上找到(https://en.wikipedia.org/wiki/WAV)
你提出的关于文件长度的第二个问题可能与AudioRecord.read
返回一个int这一事实有关,这个int是实际读取的样本数,因为它可能比你要求的少。这确实是第二个问题
答案 1 :(得分:0)
这是从OMRECORDER提取的示例implementation 'androidx.constraintlayout:constraintlayout:1.1.1'
implementation 'com.google.android.material:material:1.0.0-alpha3'
标头格式:
.WAV
以下是从WhatsappAudioRecorder中提取的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;
}
的标头格式:
.aac