【问题标题】:Android Audio Record to wavAndroid 音频录制到 wav
【发布时间】:2015-08-26 05:05:26
【问题描述】:

我使用 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 秒。

提前致谢!

【问题讨论】:

    标签: android audio audio-recording android-audiomanager android-audiorecord


    【解决方案1】:

    PCM 文件和 WAV 文件的唯一区别是 PCM 文件没有标题,而 WAV 文件有。 WAV 标头包含用于回放的关键信息,例如采样率、每个采样的位数和通道数。当您加载 PCM 文件时,应用程序必须事先了解此信息,或者您必须告诉它。例如,如果您将 PCM 文件加载到 Audacity 中,它会提示您填写所有这些内容。

    为了使现有的保存文件成为 .WAV,您需要在前面添加适当的标题。我不打算详细介绍它,因为已经有很多关于 SO 详细说明的答案,并且在网络上很容易获得 (https://en.wikipedia.org/wiki/WAV)

    您提出的关于文件长度的第二个问题可能与 AudioRecord.read 返回一个 int 的事实有关,它是实际读取的样本数,因为它可能少于您的要求。这确实是第二个问题

    【讨论】:

      【解决方案2】:

      这是从OMRECORDER 中提取的.WAV 标头格式示例:

      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;
        }
      

      以下是从WhatsappAudioRecorder 中提取的.aac 的标头格式:

      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;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多