2017-07-12 1 views
0

Der folgende Code zeichnet, spielt und dekodiert eine WAV-Datei. Wenn ich das Byte-Array aus der Datei grafisch darstellen, sieht es wie folgt aus:Warum bildet das Bytearray eine WAV-Datei, die nicht wie eine Audiowelle aussieht

Die Aufnahme ist von mir sagen: "test", die wie folgt aussehen:

enter image description here

Does Wer weiß, warum der Graph des Byte-Arrays aus der WAV-Datei nicht wie echte Audiodaten aussieht? Hier

ist der Code für die gesamte Aktivität:

package com.example.wesle.noisemachine; 

import android.content.Intent; 
import android.media.AudioFormat; 
import android.media.AudioManager; 
import android.media.AudioRecord; 
import android.net.Uri; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.view.View; 
import android.widget.Button; 
import android.media.MediaPlayer; 
import android.media.MediaRecorder; 
import android.os.Environment; 
import android.widget.Toast; 

import com.jjoe64.graphview.GraphView; 
import com.jjoe64.graphview.series.DataPoint; 
import com.jjoe64.graphview.series.LineGraphSeries; 

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.io.*; 

public class ReceiveScreen extends AppCompatActivity { 

    private Button buttonStart, buttonStop, buttonDecode, buttonPlay; 
    private String filePath; 

    private static final int RECORDER_BPP = 16; 
    private static final String AUDIO_RECORDER_FILE_EXT_WAV = ".wav"; 
    private static final String AUDIO_RECORDER_FOLDER = "AudioRecorder"; 
    private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw"; 
    private static final int RECORDER_SAMPLERATE = 44100; 
    private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO; 
    private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; 
    short[] audioData; 

    private AudioRecord recorder = null; 
    private int bufferSize = 0; 
    private Thread recordingThread = null; 
    private boolean isRecording = false; 
    Complex[] fftTempArray; 
    Complex[] fftArray; 
    int[] bufferData; 
    int bytesRecorded; 
    LineGraphSeries<DataPoint> series; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_receive_screen); 

     filePath = getFilename(); 
     final File wavfile = new File(filePath); 

     buttonStart = (Button) findViewById(R.id.buttonStart); 
     buttonStop = (Button) findViewById(R.id.buttonStop); 
     buttonPlay = (Button) findViewById(R.id.buttonPlay); 
     buttonDecode = (Button) findViewById(R.id.buttonDecode); 
     buttonStop.setEnabled(false); 
     buttonDecode.setEnabled(false); 
     buttonPlay.setEnabled(false); 

     bufferSize = AudioRecord.getMinBufferSize 
       (RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING)*3; 
     audioData = new short [bufferSize]; 

     buttonStart.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       buttonStart.setEnabled(false); 
       buttonDecode.setEnabled(false); 
       buttonPlay.setEnabled(false); 
       recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
         RECORDER_SAMPLERATE, 
         RECORDER_CHANNELS, 
         RECORDER_AUDIO_ENCODING, 
         bufferSize); 
       int i = recorder.getState(); 
       if (i==1) 
        recorder.startRecording(); 

       isRecording = true; 

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

       recordingThread.start(); 

       buttonStop.setEnabled(true); 

       Toast.makeText(getApplicationContext(), "Recording started", Toast.LENGTH_LONG).show(); 

      } 
     }); 

     buttonStop.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       buttonStop.setEnabled(false); 
       if (null != recorder){ 
        isRecording = false; 

        int i = recorder.getState(); 
        if (i==1) 
         recorder.stop(); 
        recorder.release(); 

        recorder = null; 
        recordingThread = null; 
       } 
       copyWaveFile(getTempFilename(),filePath); 
       deleteTempFile(); 

       Toast.makeText(getApplicationContext(), "Recording Completed", Toast.LENGTH_LONG).show(); 
       buttonStart.setEnabled(true); 
       buttonPlay.setEnabled(true); 
       buttonDecode.setEnabled(true); 
      } 
     }); 

     buttonPlay.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       buttonStart.setEnabled(false); 
       buttonDecode.setEnabled(false); 
       buttonPlay.setEnabled(false); 
       Toast.makeText(getApplicationContext(), "Recording Playing", Toast.LENGTH_LONG).show(); 

       Uri myUri1 = Uri.fromFile(wavfile); 
       final MediaPlayer mPlayer = new MediaPlayer(); 
       mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 

       try { 
        mPlayer.setDataSource(getApplicationContext(), myUri1); 
       } catch (IllegalArgumentException e) { 
        Toast.makeText(getApplicationContext(), "You might not set the URI correctly!", Toast.LENGTH_LONG).show(); 
       } catch (SecurityException e) { 
        Toast.makeText(getApplicationContext(), "You might not set the URI correctly!", Toast.LENGTH_LONG).show(); 
       } catch (IllegalStateException e) { 
        Toast.makeText(getApplicationContext(), "You might not set the URI correctly!", Toast.LENGTH_LONG).show(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 

       try { 
        mPlayer.prepare(); 
       } catch (IllegalStateException e) { 
        Toast.makeText(getApplicationContext(), "You might not set the URI correctly!", Toast.LENGTH_LONG).show(); 
       } catch (IOException e) { 
        Toast.makeText(getApplicationContext(), "You might not set the URI correctly!", Toast.LENGTH_LONG).show(); 
       } 

       mPlayer.start(); 

       buttonStart.setEnabled(true); 
       buttonDecode.setEnabled(true); 
       buttonPlay.setEnabled(true); 
      } 
     }); 

     buttonDecode.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       buttonStart.setEnabled(false); 
       buttonDecode.setEnabled(false); 
       buttonPlay.setEnabled(false); 

       GraphView thegraph = (GraphView) findViewById(R.id.thegraph); 
       series = new LineGraphSeries<DataPoint>(); 
       double x,y; 
       x = 0; 

       try { 
        ByteArrayOutputStream outt = new ByteArrayOutputStream(); 
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(wavfile)); 

        int read; 
        byte[] buff = new byte[1024]; 
        while ((read = in.read(buff)) > 0) 
        { 
         outt.write(buff, 0, read); 
        } 
        outt.flush(); 
        byte[] audioBytes = outt.toByteArray(); 

        //int[][] graphData = getUnscaledAmplitude(audioBytes, 1); 

        for(int i = 0; i < audioBytes.length;i++){ 


         //System.out.println(audioBytes[i]); 
         byte curByte = audioBytes[i]; 
         //int curByte = graphData[0][i]; 
         y = (curByte); 
         series.appendData(new DataPoint(x,y), true, audioBytes.length); 
         x = x + 1; 
         //x = x + (1/RECORDER_SAMPLERATE); 
        } 
        thegraph.addSeries(series); 

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

       buttonStart.setEnabled(true); 
       buttonDecode.setEnabled(true); 
       buttonPlay.setEnabled(true); 

      } 
     }); 

     //Code for the back button 
     Button backbuttonR = (Button) findViewById(R.id.backbuttonR); 
     backbuttonR.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       startActivity(new Intent(ReceiveScreen.this, MainActivity.class)); 
      } 
     }); 

    } 

    private String getFilename(){ 
     String filepath = Environment.getExternalStorageDirectory().getPath(); 
     File file = new File(filepath,AUDIO_RECORDER_FOLDER); 
     System.out.println(file.getAbsolutePath() + "/" + System.currentTimeMillis() + AUDIO_RECORDER_FILE_EXT_WAV); 
     if (!file.exists()) { 
      file.mkdirs(); 
     } 

     return (file.getAbsolutePath() + "/" + System.currentTimeMillis() + AUDIO_RECORDER_FILE_EXT_WAV); 
    } 

    private String getTempFilename() { 
     String filepath = Environment.getExternalStorageDirectory().getPath(); 
     File file = new File(filepath,AUDIO_RECORDER_FOLDER); 

     if (!file.exists()) { 
      file.mkdirs(); 
     } 

     File tempFile = new File(filepath,AUDIO_RECORDER_TEMP_FILE); 

     if (tempFile.exists()) 
      tempFile.delete(); 

     return (file.getAbsolutePath() + "/" + AUDIO_RECORDER_TEMP_FILE); 
    } 

    private void writeAudioDataToFile() { 
     byte data[] = new byte[bufferSize]; 
     String filename = getTempFilename(); 
     FileOutputStream os = null; 

     try { 
      os = new FileOutputStream(filename); 
     } catch (FileNotFoundException e) { 
      //TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     int read = 0; 
     if (null != os) { 
      while(isRecording) { 
       read = recorder.read(data, 0, bufferSize); 
       if (read > 0){ 
       } 

       if (AudioRecord.ERROR_INVALID_OPERATION != read) { 
        try { 
         os.write(data); 
        } catch (IOException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 

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

    private void deleteTempFile() { 
     File file = new File(getTempFilename()); 
     file.delete(); 
    } 

    private void copyWaveFile(String inFilename,String outFilename){ 
     FileInputStream in = null; 
     FileOutputStream out = null; 
     long totalAudioLen = 0; 
     long totalDataLen = totalAudioLen + 36; 
     long longSampleRate = RECORDER_SAMPLERATE; 
     int channels = 2; 
     long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels/8; 

     byte[] data = new byte[bufferSize]; 

     try { 
      in = new FileInputStream(inFilename); 
      out = new FileOutputStream(outFilename); 
      totalAudioLen = in.getChannel().size(); 
      totalDataLen = totalAudioLen + 36; 

      System.out.println("File size: " + totalDataLen); 

      WriteWaveFileHeader(out, totalAudioLen, totalDataLen, 
        longSampleRate, channels, byteRate); 

      while(in.read(data) != -1) { 
       out.write(data); 
      } 

      in.close(); 
      out.close(); 
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    private void WriteWaveFileHeader(
      FileOutputStream out, long totalAudioLen, 
      long totalDataLen, long longSampleRate, int channels, 
      long byteRate) throws IOException 
    { 
     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); 
    } 

    public static final class Complex { 
     // The number stored is x+I*y. 
     final private double x, y; 
     // I don't want to allow anyone to access these numbers so I've labeled 
     // them private. 

     /** Construct a point from real and imaginary parts. */ 
     public Complex(double real_part, double imaginary_part) { 
      x=real_part; 
      y=imaginary_part; 
     } 

     /** Construct a real number. */ 
     public Complex(double real_part) { 
      x=real_part; 
      y=0; 
     } 

     // A static constructor. 

     /** Construct a complex number from the given polar coordinates. */ 
     public static Complex fromPolar(double r, double theta) { 
      return new Complex(r*Math.cos(theta), r*Math.sin(theta)); 
     } 

     // Basic operations on Complex numbers. 

     /** Return the real part. */ 
     public double re(){ 
      return x; 
     } 

     /** Return the imaginary part. */ 
     public double im(){ 
      return y; 
     } 

     /** Return the complex conjugate */ 
     public Complex conj() { 
      return new Complex(x,-y); 
     } 

     /** Return the square of the absolute value. */ 
     public double absSquared() { 
      return x*x+y*y; 
     } 

     /** Return the absolute value. */ 
     public double abs() { 
      // The java.lang.Math package contains many useful mathematical functions, 
      // including the square root function. 
      return Math.sqrt(absSquared()); 
     } 

     // ARITHMETIC 

     /** Add a complex number to this one. 
     * 
     * @param z The complex number to be added. 
     * @return A new complex number which is the sum. 
     */ 
     public Complex add(Complex z) { 
      return new Complex(x+z.x, y+z.y); 
     } 

     /** Subtract a complex number from this one. 
     * 
     * @param z The complex number to be subtracted. 
     * @return A new complex number which is the sum. 
     */ 
     public Complex minus(Complex z) { 
      return new Complex(x-z.x, y-z.y); 
     } 

     /** Negate this complex number. 
     * 
     * @return The negation. 
     */ 
     public Complex neg() { 
      return new Complex(-x, -y); 
     } 

     /** Compute the product of two complex numbers 
     * 
     * @param z The complex number to be multiplied. 
     * @return A new complex number which is the product. 
     */ 
     public Complex mult(Complex z) { 
      return new Complex(x*z.x-y*z.y, x*z.y+z.x*y); 
     } 

     /** Divide this complex number by a real number. 
     * 
     * @param q The number to divide by. 
     * @return A new complex number representing the quotient. 
     */ 
     public Complex div(double q) { 
      return new Complex(x/q,y/q); 
     } 

     /** Return the multiplicative inverse. */ 
     public Complex inv() { 
      // find the square of the absolute value of this complex number. 
      double abs_squared=absSquared(); 
      return new Complex(x/abs_squared, -y/abs_squared); 
     } 

     /** Compute the quotient of two complex numbers. 
     * 
     * @param z The complex number to divide this one by. 
     * @return A new complex number which is the quotient. 
     */ 
     public Complex div(Complex z) { 
      return mult(z.inv()); 
     } 

     /** Return the complex exponential of this complex number. */ 
     public Complex exp() { 
      return new Complex(Math.exp(x)*Math.cos(y),Math.exp(x)*Math.sin(y)); 
     } 


     // FUNCTIONS WHICH KEEP JAVA HAPPY: 

     /** Returns this point as a string. 
     * The main purpose of this function is for printing the string out, 
     * so we return a string in a (fairly) human readable format. 
     */ 
     // The _optional_ override directive "@Override" below just says we are 
     // overriding a function defined in a parent class. In this case, the 
     // parent is java.lang.Object. All classes in Java have the Object class 
     // as a superclass. 
     @Override 
     public String toString() { 
      // Comments: 
      // 1) "" represents the empty string. 
      // 2) If you add something to a string, it converts the thing you 
      // are adding to a string, and then concatentates it with the string. 

      // We do some voodoo to make sure the number is displayed reasonably. 
      if (y==0) { 
       return ""+x; 
      } 
      if (y>0) { 
       return ""+x+"+"+y+"*I"; 
      } 
      // otherwise y<0. 
      return ""+x+"-"+(-y)+"*I"; 
     } 

     /** Return true if the object is a complex number which is equal to this complex number. */ 
     @Override 
     public boolean equals(Object obj) { 
      // Return false if the object is null 
      if (obj == null) { 
       return false; 
      } 
      // Return false if the object is not a Complex number 
      if (!(obj instanceof Complex)) { 
       return false; 
      } 

      // Now the object must be a Complex number, so we can convert it to a 
      // Complex number. 
      Complex other = (Complex) obj; 

      // If the x-coordinates are not equal, then return false. 
      if (x != other.x) { 
       return false; 
      } 
      // If the y-coordinates are not equal, then return false. 
      if (y != other.y) { 
       return false; 
      } 
      // Both parts are equal, so return true. 
      return true; 
     } 
     @Override 
     public int hashCode() { 
      int hash = 3; 
      hash = 83 * hash + (int) (Double.doubleToLongBits(this.x)^(Double.doubleToLongBits(this.x) >>> 32)); 
      hash = 83 * hash + (int) (Double.doubleToLongBits(this.y)^(Double.doubleToLongBits(this.y) >>> 32)); 
      return hash; 
     } 
    } 

    public int[][] getUnscaledAmplitude(byte[] eightBitByteArray, int nbChannels) 
    { 
     int[][] toReturn = new int[nbChannels][eightBitByteArray.length/(2 * nbChannels)]; 
     int index = 0; 

     for (int audioByte = 0; audioByte < eightBitByteArray.length;) 
     { 
      for (int channel = 0; channel < nbChannels; channel++) 
      { 
       // Do the byte to sample conversion. 
       int low = (int) eightBitByteArray[audioByte]; 
       audioByte++; 
       int high = (int) eightBitByteArray[audioByte]; 
       audioByte++; 
       int sample = (high << 8) + (low & 0x00ff); 

       toReturn[channel][index] = sample; 

       if (audioByte == 0) { 
        System.out.println("CHANNEL COUNT"); 
       } 
      } 
      index++; 
     } 

     return toReturn; 
    } 



} 
+1

Nun, es ist 16-Bit und in Stereo. Sie benötigen also zwei Graphen, von denen jeder einen Wert für 16 Bits und keinen Wert pro Byte erstellt. –

+0

Wissen Sie, wie ich das Byte-Array in das 16-Bit-Werte-Array übertragen kann? –

+0

Nun könnte man es manuell machen, indem man ein 'short' der richtigen Größe erzeugt und jedes Paar Bytes in ein' short' umwandelt (und dann Stereo im Kopf behält). Es gibt keinen Zweifel, SO fragen sich schon solche Dinge. Wenn Sie nio verwenden können, werden Ihnen ByteBuffer und ShortBuffer wahrscheinlich helfen. –

Antwort

0

Da scheint, Sie lesen es von Anfang an und nimmt nicht berücksichtigt .wav Datei header. Sie sollten Header lesen und dann Daten gemäß Header-Spezifikationen lesen, wie in this oder that oder vielen anderen Beispielen.

2

Wenn dies 16-Bit-Audio ist, dann ist jedes zweite Byte die niedrigstwertige Daten. Wenn Sie diese Bytes zeichnen, sieht es sehr laut aus (wie oben beschrieben).

Denken Sie daran, in einem Telefonbuch sind die Namen der Menschen nach Nachnamen, Vorname sortiert. Dies ist ähnlich wie die zwei Bytes für 16-Bit-Audio angeordnet sind. Lassen Sie uns die Namen sagen, sind:

  • Anders, Zoey
  • Anderson, Sally
  • Andrews, Craig

Wenn Sie nur die Nachnamen zu "Handlung", würden sie eine sehr haben reibungsloser Ablauf von Anders zu Anderson zu Andrews. Wenn Sie jedoch auch die Vornamen in Ihre Handlung aufnehmen würden, würde es von "Anders" zu "Zoey" springen und dann wieder runter zu "Anderson" und dann zu "Sally" und so weiter.

+0

Wissen Sie genau, was ich brauche, um die Audiodaten korrekt darzustellen? –

+0

@WesSummers können Sie jedes zweite Byte (Index 0, 2, 4, usw.) überspringen oder alle zwei Bytes zu einer Ganzzahl zusammenführen. –

+0

ich bin ein Java-Amateur, können Sie mir Code für diese zweite Sache sagen? –

Verwandte Themen