2012-02-07 2 views
33

Я использую AudioRecord для записи 16-битных данных PCM в android. После записи данных и сохранения их в файл я прочитал его, чтобы сохранить его как .wav-файл.Запись записанных данных PCM в файл .wav (java android)

Проблема в том, что файлы WAV распознаются медиаплеерами, но не играют ничего, кроме чистого шума. Мое лучшее предположение на данный момент состоит в том, что мои заголовки wav-файлов неверны, но я не мог понять, в чем проблема. (Я думаю, что это потому, что я могу играть исходные данные PCM, которые я записал в Audacity)

Вот мой код для чтения файла необработанного PCM и сохранение его в качестве .wav:

private void properWAV(File fileToConvert, float newRecordingID){ 
    try { 
     long mySubChunk1Size = 16; 
     int myBitsPerSample= 16; 
     int myFormat = 1; 
     long myChannels = 1; 
     long mySampleRate = 22100; 
     long myByteRate = mySampleRate * myChannels * myBitsPerSample/8; 
     int myBlockAlign = (int) (myChannels * myBitsPerSample/8); 

     byte[] clipData = getBytesFromFile(fileToConvert); 

     long myDataSize = clipData.length; 
     long myChunk2Size = myDataSize * myChannels * myBitsPerSample/8; 
     long myChunkSize = 36 + myChunk2Size; 

     OutputStream os;   
     os = new FileOutputStream(new File("/sdcard/onefile/assessor/OneFile_Audio_"+ newRecordingID+".wav")); 
     BufferedOutputStream bos = new BufferedOutputStream(os); 
     DataOutputStream outFile = new DataOutputStream(bos); 

     outFile.writeBytes("RIFF");         // 00 - RIFF 
     outFile.write(intToByteArray((int)myChunkSize), 0, 4);  // 04 - how big is the rest of this file? 
     outFile.writeBytes("WAVE");         // 08 - WAVE 
     outFile.writeBytes("fmt ");         // 12 - fmt 
     outFile.write(intToByteArray((int)mySubChunk1Size), 0, 4); // 16 - size of this chunk 
     outFile.write(shortToByteArray((short)myFormat), 0, 2);  // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation 
     outFile.write(shortToByteArray((short)myChannels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???) 
     outFile.write(intToByteArray((int)mySampleRate), 0, 4);  // 24 - samples per second (numbers per second) 
     outFile.write(intToByteArray((int)myByteRate), 0, 4);  // 28 - bytes per second 
     outFile.write(shortToByteArray((short)myBlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels 
     outFile.write(shortToByteArray((short)myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24 
     outFile.writeBytes("data");         // 36 - data 
     outFile.write(intToByteArray((int)myDataSize), 0, 4);  // 40 - how big is this data chunk 
     outFile.write(clipData);         // 44 - the actual data itself - just a long string of numbers 

     outFile.flush(); 
     outFile.close(); 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

} 


private static byte[] intToByteArray(int i) 
    { 
     byte[] b = new byte[4]; 
     b[0] = (byte) (i & 0x00FF); 
     b[1] = (byte) ((i >> 8) & 0x000000FF); 
     b[2] = (byte) ((i >> 16) & 0x000000FF); 
     b[3] = (byte) ((i >> 24) & 0x000000FF); 
     return b; 
    } 

    // convert a short to a byte array 
    public static byte[] shortToByteArray(short data) 
    { 
     /* 
     * NB have also tried: 
     * return new byte[]{(byte)(data & 0xff),(byte)((data >> 8) & 0xff)}; 
     * 
     */ 

     return new byte[]{(byte)(data & 0xff),(byte)((data >>> 8) & 0xff)}; 
    } 

Я не включая getBytesFromFile(), поскольку он занимает слишком много места и является проверенным и проверенным методом. Во всяком случае, вот код, который делает фактическую запись:

public void run() { 
    Log.i("ONEFILE", "Starting main audio capture loop..."); 

    int frequency = 22100; 
    int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 
    int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 

    final int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); 

    AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize); 

    audioRecord.startRecording(); 
    ByteArrayOutputStream recData = new ByteArrayOutputStream(); 
    DataOutputStream dos = new DataOutputStream(recData); 

    short[] buffer = new short[bufferSize]; 
    audioRecord.startRecording(); 

    while (!stopped) { 
     int bufferReadResult = audioRecord.read(buffer, 0, bufferSize); 

     for(int i = 0; i < bufferReadResult;i++) { 
      try { 
       dos.writeShort(buffer[i]); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
    audioRecord.stop(); 
    try { 
     dos.flush(); 
     dos.close(); 
    } catch (IOException e1) { 
     e1.printStackTrace(); 
    } 

    audioRecord.stop(); 

    byte[] clipData = recData.toByteArray(); 

    File file = new File(audioOutputPath); 
    if(file.exists()) 
     file.delete(); 
    file = new File(audioOutputPath); 
    OutputStream os; 
    try { 
     os = new FileOutputStream(file); 

     BufferedOutputStream bos = new BufferedOutputStream(os); 
     DataOutputStream outFile = new DataOutputStream(bos); 

     outFile.write(clipData); 

     outFile.flush(); 
     outFile.close(); 

    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

Пожалуйста, предложите что может идти не так.

+0

Не могли бы вы показать, что ваш заголовок выглядит. E.G первые 48 байтов (3 строки, по 16 байт в каждом) отображаются как значения HEX: например. 52 49 46 46 E1 C3 ... – Zelimir

ответ

14

Я боролся с этим точно таким же вопросом в течение нескольких часов в настоящее время, и мой вопрос был главным, что при записи в 16 бите, вы должны быть очень осторожны, о том, что вы пишете на выход. WAV-файл ожидает данные в формате Little Endian, но с помощью writeShort записывает его на выходе в виде Big Endian. У меня также были интересные результаты при использовании других функций, поэтому я вернулся к написанию байтов в правильном порядке и это работает.

Я широко использовал редактор Hex, отлаживая это. Я могу порекомендовать вам сделать то же самое. Кроме того, заголовок в ответе выше работает, я использовал его для проверки по сравнению с моим собственным кодом, и этот заголовок довольно надежный.

4

Вы уверены, что находитесь в байтовом порядке? «RIFF», «WAV», «fmt» и «data» выглядят отлично, но числа в заголовке могут потребоваться для другого порядка (маленький конец и большой endian). Вам также не нужно вручную преобразовывать байты, используя ваш метод intToByteArray. Вы можете использовать методы writeInt и writeShortDataOutputStream. Для первого, это будет выглядеть что-то вроде:

outFile.writeInt(Integer.reverseBytes((int)myChunkSize));

Для трусах, было бы как:

outFile.writeShort(Short.reverseBytes((short)myFormat))

Таким образом, вы также не нужно, чтобы обеспечить смещение и длина (0, 4) номеров. Мило.

8

В соответствии с заголовком, я следил за этим кодом (если это вам поможет).

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) (2 * 16/8); // block align 
     header[33] = 0; 
     header[34] = RECORDER_BPP; // 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); 

     out.write(header, 0, 44); 
+2

Можете ли вы предоставить более подробную информацию (например, возможные значения) для переменных (например, 'totalDataLen',' byteRate') и как они связаны друг с другом? Кроме того, значение 'header [32]' не зависит от каких-либо переменных? – Kaarel

+2

Это не будет работать точно так же, как стоит, не зная, как заполнить эти переменные. Вы можете найти эту информацию [здесь] (http://soundfile.sapp.org/doc/WaveFormat/). Кроме того, количество каналов неверно, если значение> 255. Как ни мало похоже, я работаю над проектами, использующими произвольные входные каналы и хранящимися как WAV, поэтому ваш писатель не будет работать. Поля должны быть: 'header [22] = (byte) (channels & 0xFF); header [23] = (байт) ((каналы >> 8) & 0xFF); Также, еще одно улучшение: 'header [32] = (байт) ((каналы * RECORDER_BPP)/8); // block align' – EntangledLoops

+0

Спасибо! помогли подчеркнуть, что для «fmt» требуется пространство –

2

Как правильно утверждает Рональд Куненборг, проблема заключается в преобразовании Litte Endian/Big Endian.

Самый простой способ, чтобы написать короткий помощника, как это:

public static void writeShortLE(DataOutputStream out, short value) { 
    out.writeByte(value & 0xFF); 
    out.writeByte((value >> 8) & 0xFF); 
} 

Это очень полезно, если вы Записывайте аудио для волнового файла с Android, и вы нуждаетесь в короткий массива тоже.

(Credits: https://stackoverflow.com/a/1394839/1686216)

 Смежные вопросы

  • Нет связанных вопросов^_^