2016-05-17 15 views
3

Ich entwickle gerade eine Android-Anwendung, die Audioaufnahme und -wiedergabe hat. Ich bin neu im Umgang mit Audio und habe Probleme mit der Codierung und den Formaten.Wie konvertiert man .pcm Datei in .wav oder .mp3?

Ich kann das Audio in meiner Anwendung aufnehmen und abspielen, aber beim Exportieren kann ich das Audio nicht wiedergeben. Die einzige Möglichkeit, die ich gefunden habe, war das Exportieren meiner .pcm-Datei und das Konvertieren mit Audacity.

Dies ist mein Code, um die Audioaufnahme ist:

private Thread recordingThread 
private AudioRecord mRecorder; 
private boolean isRecording = false; 

private void startRecording() { 

    mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
      Constants.RECORDER_SAMPLERATE, Constants.RECORDER_CHANNELS, 
      Constants.RECORDER_AUDIO_ENCODING, Constants.BufferElements2Rec * Constants.BytesPerElement); 

    mRecorder.startRecording(); 
    isRecording = true; 

    recordingThread = new Thread(new Runnable() { 
     public void run() { 
      writeAudioDataToFile(); 
     } 
    }, "AudioRecorder Thread"); 
    recordingThread.start(); 
} 

private void writeAudioDataToFile() { 
    // Write the output audio in byte 

    FileOutputStream os = null; 
    try { 
     os = new FileOutputStream(mFileName); 
    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } 

    while (isRecording) { 
     // gets the voice output from microphone to byte format 
     mRecorder.read(sData, 0, Constants.BufferElements2Rec); 
     try { 
      // // writes the data to file from buffer 
      // // stores the voice buffer 

      byte bData[] = short2byte(sData); 

      os.write(bData, 0, Constants.BufferElements2Rec * Constants.BytesPerElement); 

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
    try { 
     os.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

das aufgenommene Audio Um zu spielen, ist der Code:

private void startPlaying() { 

    new Thread(new Runnable() { 
     public void run() { 

      try { 

       File file = new File(mFileName); 

       byte[] audioData = null; 

       InputStream inputStream = new FileInputStream(mFileName); 
       audioData = new byte[Constants.BufferElements2Rec]; 

       mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, Constants.RECORDER_SAMPLERATE, 
         AudioFormat.CHANNEL_OUT_MONO, Constants.RECORDER_AUDIO_ENCODING, 
         Constants.BufferElements2Rec * Constants.BytesPerElement, AudioTrack.MODE_STREAM); 


       final float duration = (float) file.length()/Constants.RECORDER_SAMPLERATE/2; 

       Log.i(TAG, "PLAYBACK AUDIO"); 
       Log.i(TAG, String.valueOf(duration)); 


       mPlayer.setPositionNotificationPeriod(Constants.RECORDER_SAMPLERATE/10); 
       mPlayer.setNotificationMarkerPosition(Math.round(duration * Constants.RECORDER_SAMPLERATE)); 

       mPlayer.play(); 

       int i = 0; 
       while ((i = inputStream.read(audioData)) != -1) { 
        try { 
         mPlayer.write(audioData, 0, i); 
        } catch (Exception e) { 
         Log.e(TAG, "Exception: " + e.getLocalizedMessage()); 
        } 
       } 

      } catch (FileNotFoundException fe) { 
       Log.e(TAG, "File not found: " + fe.getLocalizedMessage()); 
      } catch (IOException io) { 
       Log.e(TAG, "IO Exception: " + io.getLocalizedMessage()); 
      } 

     } 

    }).start(); 


} 

Die Konstanten in einer Konstanten Klasse definiert sind:

public class Constants { 

    final static public int RECORDER_SAMPLERATE = 44100; 
    final static public int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO; 
    final static public int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; 

    final static public int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024 
    final static public int BytesPerElement = 2; // 2 bytes in 16bit format 


} 

Wenn ich die Datei exportieren, wie es ist, konvertiere ich es mit Audacity und es spielt. Ich muss es jedoch in einem Format exportieren, das automatisch abgespielt werden kann.

Ich habe Antworten zur Implementierung von Lame gesehen und arbeite gerade daran. Ich habe auch festgestellt, eine Antwort zu konvertieren mit:

private File rawToWave(final File rawFile, final String filePath) throws IOException { 

    File waveFile = new File(filePath); 

    byte[] rawData = new byte[(int) rawFile.length()]; 
    DataInputStream input = null; 
    try { 
     input = new DataInputStream(new FileInputStream(rawFile)); 
     input.read(rawData); 
    } finally { 
     if (input != null) { 
      input.close(); 
     } 
    } 

    DataOutputStream output = null; 
    try { 
     output = new DataOutputStream(new FileOutputStream(waveFile)); 
     // WAVE header 
     // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 
     writeString(output, "RIFF"); // chunk id 
     writeInt(output, 36 + rawData.length); // chunk size 
     writeString(output, "WAVE"); // format 
     writeString(output, "fmt "); // subchunk 1 id 
     writeInt(output, 16); // subchunk 1 size 
     writeShort(output, (short) 1); // audio format (1 = PCM) 
     writeShort(output, (short) 1); // number of channels 
     writeInt(output, Constants.RECORDER_SAMPLERATE); // sample rate 
     writeInt(output, Constants.RECORDER_SAMPLERATE * 2); // byte rate 
     writeShort(output, (short) 2); // block align 
     writeShort(output, (short) 16); // bits per sample 
     writeString(output, "data"); // subchunk 2 id 
     writeInt(output, rawData.length); // subchunk 2 size 
     // Audio data (conversion big endian -> little endian) 
     short[] shorts = new short[rawData.length/2]; 
     ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts); 
     ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2); 
     for (short s : shorts) { 
      bytes.putShort(s); 
     } 
     output.write(bytes.array()); 
    } finally { 
     if (output != null) { 
      output.close(); 
     } 
    } 

    return waveFile; 

} 

private void writeInt(final DataOutputStream output, final int value) throws IOException { 
    output.write(value >> 0); 
    output.write(value >> 8); 
    output.write(value >> 16); 
    output.write(value >> 24); 
} 

private void writeShort(final DataOutputStream output, final short value) throws IOException { 
    output.write(value >> 0); 
    output.write(value >> 8); 
} 

private void writeString(final DataOutputStream output, final String value) throws IOException { 
    for (int i = 0; i < value.length(); i++) { 
     output.write(value.charAt(i)); 
    } 
} 

Aber das, wenn exportiert, spielt mit der richtigen Dauer aber nur weißes Rauschen.

Einige der Antworten, die ich versucht habe, aber zur Arbeit war nicht in der Lage: kann

Wer darauf hinweisen, was das Beste ist, Lösung? Ist es wirklich lahm implementiert oder kann es auf eine geradlinigere Weise getan werden? Wenn ja, warum konvertiert das Codebeispiel die Datei in nur weißes Rauschen?

Antwort

1

Nur um zu registrieren, löste ich meine Notwendigkeit der Aufnahme eines Audio in gängigen Playern mit MediaRecorder statt Audio Recorder.

Um die Aufnahme zu starten:

MediaRecorder mRecorder = new MediaRecorder(); 
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 
    mRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB); 
    mRecorder.setOutputFile(Environment.getExternalStorageDirectory() 
       .getAbsolutePath() + "/recording.3gp"); 

    mRecorder.prepare(); 
    mRecorder.start(); 

Und um die Aufnahme zu spielen:

mPlayer = new MediaPlayer(); 
    mPlayer.setDataSource(Environment.getExternalStorageDirectory() 
       .getAbsolutePath() + "/recording.3gp"); 

    mPlayer.prepare(); 
    mPlayer.start(); 
9

Sie haben die meisten der Code korrekt bekam. Das einzige Problem, das ich sehen kann, ist der Teil, wo Sie die PCM-Daten in die WAV-Datei schreiben. Dies sollte ziemlich einfach sein, weil WAV = Metadata + PCM (in dieser Reihenfolge). Dies sollte funktionieren:

private void rawToWave(final File rawFile, final File waveFile) throws IOException { 

    byte[] rawData = new byte[(int) rawFile.length()]; 
    DataInputStream input = null; 
    try { 
     input = new DataInputStream(new FileInputStream(rawFile)); 
     input.read(rawData); 
    } finally { 
     if (input != null) { 
      input.close(); 
     } 
    } 

    DataOutputStream output = null; 
    try { 
     output = new DataOutputStream(new FileOutputStream(waveFile)); 
     // WAVE header 
     // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 
     writeString(output, "RIFF"); // chunk id 
     writeInt(output, 36 + rawData.length); // chunk size 
     writeString(output, "WAVE"); // format 
     writeString(output, "fmt "); // subchunk 1 id 
     writeInt(output, 16); // subchunk 1 size 
     writeShort(output, (short) 1); // audio format (1 = PCM) 
     writeShort(output, (short) 1); // number of channels 
     writeInt(output, 44100); // sample rate 
     writeInt(output, RECORDER_SAMPLERATE * 2); // byte rate 
     writeShort(output, (short) 2); // block align 
     writeShort(output, (short) 16); // bits per sample 
     writeString(output, "data"); // subchunk 2 id 
     writeInt(output, rawData.length); // subchunk 2 size 
     // Audio data (conversion big endian -> little endian) 
     short[] shorts = new short[rawData.length/2]; 
     ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts); 
     ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2); 
     for (short s : shorts) { 
      bytes.putShort(s); 
     } 

     output.write(fullyReadFileToBytes(rawFile)); 
    } finally { 
     if (output != null) { 
      output.close(); 
     } 
    } 
} 
    byte[] fullyReadFileToBytes(File f) throws IOException { 
    int size = (int) f.length(); 
    byte bytes[] = new byte[size]; 
    byte tmpBuff[] = new byte[size]; 
    FileInputStream fis= new FileInputStream(f); 
    try { 

     int read = fis.read(bytes, 0, size); 
     if (read < size) { 
      int remain = size - read; 
      while (remain > 0) { 
       read = fis.read(tmpBuff, 0, remain); 
       System.arraycopy(tmpBuff, 0, bytes, size - remain, read); 
       remain -= read; 
      } 
     } 
    } catch (IOException e){ 
     throw e; 
    } finally { 
     fis.close(); 
    } 

    return bytes; 
} 
private void writeInt(final DataOutputStream output, final int value) throws IOException { 
    output.write(value >> 0); 
    output.write(value >> 8); 
    output.write(value >> 16); 
    output.write(value >> 24); 
} 

private void writeShort(final DataOutputStream output, final short value) throws IOException { 
    output.write(value >> 0); 
    output.write(value >> 8); 
} 

private void writeString(final DataOutputStream output, final String value) throws IOException { 
    for (int i = 0; i < value.length(); i++) { 
     output.write(value.charAt(i)); 
    } 
} 

Wie

verwenden Es ist ganz einfach zu bedienen.nennen Sie es einfach wie folgt aus:

File f1 = new File("/sdcard/44100Sampling-16bit-mono-mic.pcm"); // The location of your PCM file 
    File f2 = new File("/sdcard/44100Sampling-16bit-mono-mic.wav"); // The location where you want your WAV file 
    try { 
    rawToWave(f1, f2); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 

Wie das alles

funktioniert Wie Sie der einzige Unterschied zwischen WAV und PCM-Dateiformaten sehen können, ist die WAV-Header. Die Annahme ist, dass Sie 16 Bit PCM MONO Audio aufnehmen (was nach Ihrem Code, Sie sind). Die rawToWave-Funktion fügt der WAV-Datei einfach Header hinzu, so dass Musik-Player wissen, was zu erwarten ist, wenn Ihre Datei geöffnet wird, und nach den Headern schreibt sie einfach die PCM-Daten ab dem letzten Bit.

kühle Spitze

Wenn Sie die Tonhöhe Ihrer Stimme verschieben möchten, oder eine Sprachwechsler App machen, alles, was Sie zu tun ist erhöhen/den Wert von writeInt(output, 44100); // sample rate in Ihrem Code zu verringern. Wenn Sie den Wert verringern, wird der Player aufgefordert, ihn mit einer anderen Rate abzuspielen und dadurch die Tonhöhe des Ausgangs zu ändern. Nur ein kleines Extra "Gut zu wissen". :)

+0

@filipebarretto Ich würde es wirklich schätzen, wenn Sie die obige Antwort akzeptieren könnten, wenn Sie der Meinung sind, dass Ihre Frage zur Konvertierung von .pcm in .wav/.mp3 ausreichend beantwortet wurde? –

1

Ich weiß, es ist spät und Sie haben Ihre Sachen mit MediaRecorder arbeiten. Aber ich dachte darüber nach, meine Antwort zu teilen, denn ich brauchte eine gute Zeit, um sie zu finden. :)

Wenn Sie Ihr Audio aufzeichnen, sind die Daten read so kurz von Ihrem AudioRecord Objekt und es wird dann in Bytes vor dem Speichern in der Datei .pcm konvertiert.

Jetzt, wenn Sie die Datei .wav schreiben, machen Sie wieder die kurze Konvertierung. Dies ist nicht erforderlich. Also, in Ihrem Code, wenn Sie den folgenden Block entfernen und schreiben Sie die rawData direkt an das Ende der .wav Datei. Es wird gut funktionieren.

short[] shorts = new short[rawData.length/2]; 
    ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts); 
    ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2); 
    for (short s : shorts) { 
     bytes.putShort(s); 
    } 

Überprüfen Sie den folgenden Code, den Sie nach dem Entfernen des doppelten Codeblocks erhalten.