2017-07-12 28 views
0

下面的代碼記錄,播放和「解碼」了一個wav文件。當我繪製從該文件的字節數組,它看起來像這樣:爲什麼字節數組形成的wav文件看起來不像音頻波形

記錄是我說「測試」,這應該是這樣的:

enter image description here

不任何人都知道爲什麼wav文件中字節數組的圖形看起來不像真正的audiodata?

這裏是整個活動的代碼:

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

這是16位和立體聲。所以你實際上需要兩個圖形,每個圖形每16位構成一個值,而不是每個字節的值。 –

+0

你知道我可以如何將字節數組傳送到16位值數組嗎? –

+0

那麼你可以手動做到這一點,創建一個合適大小的「short」,並將每一對字節轉換爲「short」(然後記住軸承立體聲)。毫無疑問,這些問題已經存在。如果你可以使用nio,那麼ByteBuffer和ShortBuffer可能會幫助你。 –

回答

0

因爲,看來,你從開始讀它並沒有考慮到.wav文件header。您應該讀取標題,然後根據標題規範讀取數據,如thisthat或許多其他示例。

2

如果這是16位音頻,那麼每隔一個字節將是最不重要的數據。繪製這些字節會使它看起來非常嘈雜(如上所述)。

想想這樣,在電話簿中,人們的姓名是按姓氏,名字排序的。這與兩個字節如何爲16位音頻排序相似。比方說,名稱是:

  • 安德斯,佐伊
  • 安德森,薩利
  • 安德魯斯,克雷格

如果你是「暗算」只有最後的名字,他們將有一個非常順利從安德斯到安德森,再到安德魯斯。但是,如果您還包含劇情中的名字,它會從「Anders」跳轉到「Zoey」,然後跳轉到「Anderson」,然後跳轉到「Sally」,依此類推。

+0

你知道我需要什麼來正確地繪製音頻數據嗎? –

+0

@WesSummers可以跳過每隔一個字節(使用索引0,2,4等)或將每兩個字節合併爲一個整數。 –

+0

即時通訊java業餘愛好者,你能告訴我代碼的第二件事嗎? –