Java校音器:采集声音以及mathematica演示

上节我们讲了怎么写fft,这次我们来实践下fft的应用。

前天又开始唱从初二唱到大二的晴天,由于长期音不准,于是干脆借此做一个算音调的软件来校准自己的唱歌音调。

首先,是蛋疼的一个工作,读入麦克风。这个着实让我折腾很久,使用java.sound的接口,终于完成了采集。

一,采集

其实很简单(虽然搞了一天),抄了抄demo,发现只需要直接捕捉line。

  1.         TargetDataLine line;
  2.         AudioFormat format;
  3.         format=new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,44100,16,1,(16/8),44100,true);//定声音格式为44100hz采样,单通道
  4.         DataLine.Info info = new DataLine.Info(TargetDataLine.class, 
  5.                 format);
  6.         if (!AudioSystem.isLineSupported(info)) //监测是否支持
  7.         {
  8.             System.out.println("Line matching " + info + " not supported.");
  9.         }
  10.  
  11.         line = (TargetDataLine) AudioSystem.getLine(info);//打开line
  12.         line.open(format, line.getBufferSize());
  13.         ByteArrayOutputStream out = new ByteArrayOutputStream();
  14.         int frameSizeInBytes = format.getFrameSize();
  15.         int bufferLengthInFrames = line.getBufferSize() / 8;
  16.         int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
  17.         byte[] data = new byte[bufferLengthInBytes];
  18.         int numBytesRead;
  19.         line.start();//打开轨道采集
  20.         long st,tm;
  21.         st=System.currentTimeMillis();
  22.         tm=st;
  23.         //System.out.format("leninbd:%d\n",bufferLengthInBytes);
  24.         System.out.println("Wait");
  25.         while(tm-st<3000)
  26.             tm=System.currentTimeMillis();
  27.         System.out.println("Run");
  28.         st=System.currentTimeMillis();//一个准备时间
  29.         tm=st;
  30.         while(tm-st<2000)//读两秒的数据
  31.         {
  32.             if((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) 
  33.             {
  34.                 break;
  35.             }
  36.             out.write(data, 0, numBytesRead);
  37.             tm=System.currentTimeMillis();
  38.         }
  39.         //System.out.println("Done");
  40.         line.stop();
  41.         line.close();
  42.         line = null;
  43.         out.flush();
  44.         out.close();
  45.  //完成采集
  46.         int audioint[] = convert(format,out.toByteArray());
  47.         //将byte转化为int
  48.         PrintWriter pw=new PrintWriter(new FileWriter(new File("dt.txt")),true);
  49.         for(int i=0;i<audioint.length;i++)
  50.            pw.format("%d ",audioint[i]);
  51.         }

其中转化算法如下(不要问我什么原理,这种东西我一般都抄的。。。没什么深入了解的意义)

  1.     int [] convert(AudioFormat format,byte[] audioBytes)
  2.     {
  3.         int[] audioData = null;
  4.         if (format.getSampleSizeInBits() == 16) 
  5.         {
  6.             int nlengthInSamples = audioBytes.length / 2;
  7.             audioData = new int[nlengthInSamples];
  8.             if (format.isBigEndian()) 
  9.             {
  10.                 for (int i = 0; i < nlengthInSamples; i++) 
  11.                 {
  12.                     /* First byte is MSB (high order) */
  13.                     int MSB = (int) audioBytes[2*i];
  14.                     /* Second byte is LSB (low order) */
  15.                     int LSB = (int) audioBytes[2*i+1];
  16.                     audioData[i] = MSB << 8 | (255 > LSB);
  17.                 }
  18.             } 
  19.             else 
  20.             {
  21.                 for (int i = 0; i < nlengthInSamples; i++) 
  22.                 {
  23.                     /* First byte is LSB (low order) */
  24.                     int LSB = (int) audioBytes[2*i];
  25.                     /* Second byte is MSB (high order) */
  26.                     int MSB = (int) audioBytes[2*i+1];
  27.                     audioData[i] = MSB << 8 | (255 > LSB);
  28.                 }
  29.             }
  30.         } 
  31.         else if (format.getSampleSizeInBits() == 8) 
  32.         {
  33.             int nlengthInSamples = audioBytes.length;
  34.             audioData = new int[nlengthInSamples];
  35.             if (format.getEncoding().toString().startsWith("PCM_SIGN")) 
  36.             {
  37.                 for (int i = 0; i < audioBytes.length; i++) 
  38.                 {
  39.                     audioData[i] = audioBytes[i];
  40.                 }
  41.             } 
  42.             else 
  43.             {
  44.                 for (int i = 0; i < audioBytes.length; i++) 
  45.                 {
  46.                     audioData[i] = audioBytes[i] - 128;
  47.                 }
  48.             }
  49.         }
  50.         return audioData;
  51.     }

于是就完成了声音的基础采集,拿去放到mathematica测试,用吉他弹几个音嘛

  1. (*Mathematica Code*)
  2. Init[] :=
  3.  (dt = 
  4.    Import["/Users/none/Develop/mph/build/dt.txt", "Table"][[1]];
  5.   ListLinePlot[dt, PlotRange -> All])
  6.  
  7. Anal := (
  8.   (*dt=SignalMk[1,263];*)
  9.   ts = Length[dt]/44100;
  10.   dr = 1000;
  11.   ListLinePlot[
  12.     Abs[     
  13.      Fourier[dt[[100 ;;      Length[dt]     ]]         ][[1 ;; 
  14.         Round[dr*ts] ]]],
  15.     PlotRange -> All
  16.     , DataRange -> {1, dr*ts}*1/ts]
  17.    1
  18.   )

分析下得到两个图

analse

 

(其实是晴天的第一个小结啦)

analse2

 

频谱几乎正确,虽然不知道200hz哪来的音啊。。。。。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注