Android аудио запись в WAV

Я записал звук с помощью диктофона на Android, и он создает сырой файл PCM. Я пытаюсь преобразовать его в формат, который я могу слушать (например, wav или mp3.

Я начал с этого примера, но не знаю, куда идти дальше: пример Android AudioRecord

попытался выполнить следующие действия: http://computermusicblog.com/blog/2008/08/29/reading-and-writing-wav-files-in-java

Запись.Wav с 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 секунд, это было бы здорово.

Заранее спасибо!

2 ответа

Решение

Единственная разница между файлом PCM и файлом WAV заключается в том, что у файла PCM нет заголовка, а у файла WAV - нет. Заголовок WAV содержит ключевую информацию для воспроизведения, такую ​​как частота дискретизации, количество бит на выборку и количество каналов. Когда вы загружаете файл PCM, либо приложение должно предварительно знать эту информацию, либо вы должны сообщить об этом. Например, если вы загрузите файл PCM в Audacity, вам будет предложено заполнить все эти элементы.

Чтобы сделать существующий файл сохранения в формате.WAV, необходимо добавить соответствующий заголовок. Я не буду вдаваться в подробности об этом, потому что на SO уже есть много ответов, детализирующих его, и он легко доступен в Интернете ( https://en.wikipedia.org/wiki/WAV)

Вторая проблема, которую вы поднимаете о длине файла, может быть связана с тем, что AudioRecord.read возвращает int, которое является фактически прочитанным количеством сэмплов, поскольку оно может быть меньше, чем вы запрашивали. Это действительно второй вопрос, хотя

Это образец .WAV Формат заголовка Извлечено из 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;
  }

Ниже приведен формат заголовка для .aac извлечено из 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;
    }
Другие вопросы по тегам