2017-07-12 27 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业余爱好者,你能告诉我代码的第二件事吗? –