2013-07-02 6 views
2

현재 VS2012에 메트로 앱을 사용 중입니다. 나는 사용자의 음성을 녹음하고 wav 파일 (16 비트, 44.1kHz, 모노)에 저장하는 C# 코드를 가지고있다. 아래에 표시된대로 -1과 1 사이의 값을 갖는 double 배열 데이터 만 포함하도록 pcm을 처리했습니다.pcm 데이터에 FFT 적용 및 스펙트로 그램으로 변환

다음 단계는 이중 배열 데이터에 FFT를 적용하여 스펙트로 그램으로 변환하는 것입니다. 라이브러리를 사용하지 않고 이중 배열을 사용하는 FFT 알고리즘이 있는지 알고 싶습니다.

이 데이터 (FFT 적용 후)를 메트로를 사용하는 스펙트로 그램 (나중에 다른 스펙트로 그램과 비교하는 데 사용됨)으로 변환하는 방법이 있는지 알고 싶습니다.

주파수를 가져 와서 시각적으로 보여주기 위해 FFT를 적용하는 데 사용하려는 double 배열 값 중 일부의 예입니다.

이것은 내 pcm 데이터를 처리하는 코드입니다.

public static Double[] prepare(String wavePath) 
    { 
     Double[] data; 
     byte[] wave; 
     byte[] sR = new byte[4]; 
     System.IO.FileStream WaveFile = System.IO.File.OpenRead(wavePath); 
     wave = new byte[WaveFile.Length]; 
     data = new Double[(wave.Length - 44)/4];//shifting the headers out of the PCM data; 
     WaveFile.Read(wave, 0, Convert.ToInt32(WaveFile.Length));//read the wave file into the wave variable 
     /***********Converting and PCM accounting***************/ 
     for (int i = 0; i < data.Length; i ++) 
     { 
      data[i] = BitConverter.ToInt16(wave, i*2)/32768.0; 
     } 
      //65536.0.0=2^n,  n=bits per sample; 

     return data; 
    } 

편집 * 이 내 출력 *
-3.0517578125E-05
-3.0517578125E-05
-3.0517578125E-05
-3.0517578125E-05
-6.103515625E-05
-9.1552734375e-05
-6.103515625e-05 -6.103515625E 05
-6.103515625E 05
-6.103515625E 05
-9.1552734375E 05
-6.103515625E 05
-9.1552734375E 05
-6.103515625E 05
-9.1552734375E-05
-6.103515625E-05

+0

두 번째 숫자가 모두 0 인 이유를 알고 계십니까? 이상하게 보입니다. – thalm

+0

확실하지 않습니다. 나도 좀 이상해. 어쩌면 내가 만약 내가 pcm 데이터를 처리하기 위해 사용하는 코드를 넣어 준다면, 나를 가리킬 수있다. –

+0

2를 늘리고 데이터 [i]에 쓰기 만하면 모든 두 번째 필드에만 쓸 수있다. 너는 하나씩 늘려야 해. 예 : i ++를 사용하고 BitConverter.ToInt16 (wave, i * 2)을 사용하여 바이트를 읽습니다. 그리고 나는 또한 왜 데이터를 읽을 때 PCM 헤더의 오프셋을 고려하지 않는지 궁금합니다. 44에서 시작해야 할까? – thalm

답변

2

당신이 예를 코딩 할 수 있습니다 찾을 수 C#을 FFT에 대한 구글 검색을 수행합니다. 이 하나가 아주 좋아 보였습니다. 나는 here을 발견했습니다. 흥미로운 점은 원하는 경우 복소수를 예상하는 Table FFT의 입력으로 매초 두 번째 자리에 0이있는 데이터를 사용할 수 있다는 것입니다.

항상 2 개의 샘플 수의 데이터 패키지가 있는지 확인하십시오.

/// <summary>                        
    /// Compute the forward or inverse Fourier Transform of data, with data         
    /// containing complex valued data as alternating real and imaginary          
    /// parts. The length must be a power of 2. This method caches values          
    /// and should be slightly faster on than the FFT method for repeated uses.        
    /// It is also slightly more accurate. Data is transformed in place.          
    /// </summary>                       
    /// <param name="data">The complex data stored as alternating real          
    /// and imaginary parts</param>                   
    /// <param name="forward">true for a forward transform, false for           
    /// inverse transform</param>                    
    public void TableFFT(double[] data, bool forward)               
    {                           
     var n = data.Length;                     
     // checks n is a power of 2 in 2's complement format             
     if ((n & (n - 1)) != 0)                    
      throw new ArgumentException(                  
       "data length " + n + " in FFT is not a power of 2"           
       );                       
     n /= 2; // n is the number of samples                

     Reverse(data, n); // bit index data reversal               

     // make table if needed                    
     if ((cosTable == null) || (cosTable.Length != n))              
      Initialize(n);                     

     // do transform: so single point transforms, then doubles, etc.          
     double sign = forward ? B : -B;                  
     var mmax = 1;                       
     var tptr = 0;                       
     while (n > mmax)                      
     {                          
      var istep = 2 * mmax;                    
      for (var m = 0; m < istep; m += 2)                
      {                         
       var wr = cosTable[tptr];                  
       var wi = sign * sinTable[tptr++];                
       for (var k = m; k < 2 * n; k += 2 * istep)             
       {                        
        var j = k + istep;                  
        var tempr = wr * data[j] - wi * data[j + 1];            
        var tempi = wi * data[j] + wr * data[j + 1];            
        data[j] = data[k] - tempr;                
        data[j + 1] = data[k + 1] - tempi;              
        data[k] = data[k] + tempr;                
        data[k + 1] = data[k + 1] + tempi;              
       }                        
      }                         
      mmax = istep;                      
     }                          


     // perform data scaling as needed                  
     Scale(data, n, forward);                    
    } 

    /// <summary>                        
    /// Compute the forward or inverse Fourier Transform of data, with          
    /// data containing real valued data only. The output is complex           
    /// valued after the first two entries, stored in alternating real          
    /// and imaginary parts. The first two returned entries are the real          
    /// parts of the first and last value from the conjugate symmetric          
    /// output, which are necessarily real. The length must be a power          
    /// of 2.                         
    /// </summary>                       
    /// <param name="data">The complex data stored as alternating real          
    /// and imaginary parts</param>                   
    /// <param name="forward">true for a forward transform, false for           
    /// inverse transform</param>                    
    public void RealFFT(double[] data, bool forward)               
    {                           

     var n = data.Length; // # of real inputs, 1/2 the complex length          
     // checks n is a power of 2 in 2's complement format             
     if ((n & (n - 1)) != 0)                    
      throw new ArgumentException(                  
       "data length " + n + " in FFT is not a power of 2"           
       );                       

     var sign = -1.0; // assume inverse FFT, this controls how algebra below works       
     if (forward)                       
     { // do packed FFT. This can be changed to FFT to save memory           
      TableFFT(data, true);                    
      sign = 1.0;                      
      // scaling - divide by scaling for N/2, then mult by scaling for N        
      if (A != 1)                      
      {                         
       var scale = Math.Pow(2.0, (A - 1)/2.0);              
       for (var i = 0; i < data.Length; ++i)               
        data[i] *= scale;                   
      }                         
     }                          

     var theta = B * sign * 2 * Math.PI/n;                
     var wpr = Math.Cos(theta);                   
     var wpi = Math.Sin(theta);                   
     var wjr = wpr;                      
     var wji = wpi;                      

     for (var j = 1; j <= n/4; ++j)                  
     {                          
      var k = n/2 - j;                    
      var tkr = data[2 * k]; // real and imaginary parts of t_k = t_(n/2 - j)      
      var tki = data[2 * k + 1];                  
      var tjr = data[2 * j]; // real and imaginary parts of t_j          
      var tji = data[2 * j + 1];                  

      var a = (tjr - tkr) * wji;                  
      var b = (tji + tki) * wjr;                  
      var c = (tjr - tkr) * wjr;                  
      var d = (tji + tki) * wji;                  
      var e = (tjr + tkr);                    
      var f = (tji - tki);                    

      // compute entry y[j]                    
      data[2 * j] = 0.5 * (e + sign * (a + b));               
      data[2 * j + 1] = 0.5 * (f + sign * (d - c));              

      // compute entry y[k]                    
      data[2 * k] = 0.5 * (e - sign * (b + a));               
      data[2 * k + 1] = 0.5 * (sign * (d - c) - f);              

      var temp = wjr;                     
      // todo - allow more accurate version here? make option?           
      wjr = wjr * wpr - wji * wpi;                  
      wji = temp * wpi + wji * wpr;                  
     }                          

     if (forward)                       
     {                          
      // compute final y0 and y_{N/2}, store in data[0], data[1]          
      var temp = data[0];                    
      data[0] += data[1];                    
      data[1] = temp - data[1];                   
     }                          
     else                         
     {                          
      var temp = data[0]; // unpack the y0 and y_{N/2}, then invert FFT         
      data[0] = 0.5 * (temp + data[1]);                 
      data[1] = 0.5 * (temp - data[1]);                 
      // do packed inverse (table based) FFT. This can be changed to regular inverse FFT to save memory 
      TableFFT(data, false);                   
      // scaling - divide by scaling for N, then mult by scaling for N/2        
      //if (A != -1) // todo - off by factor of 2? this works, but something seems weird    
      {                         
       var scale = Math.Pow(2.0, -(A + 1)/2.0)*2;             
       for (var i = 0; i < data.Length; ++i)               
        data[i] *= scale;                   
      }                         
     }                          
    }                           

    /// <summary>                        
    /// Determine how scaling works on the forward and inverse transforms.         
    /// For size N=2^n transforms, the forward transform gets divided by          
    /// N^((1-a)/2) and the inverse gets divided by N^((1+a)/2). Common          
    /// values for (A,B) are                     
    ///  (0, 1) - default                    
    ///  (-1, 1) - data processing                  
    ///  (1,-1) - signal processing                  
    /// Usual values for A are 1, 0, or -1                 
    /// </summary>                       
    public int A { get; set; }                    

    /// <summary>                        
    /// Determine how phase works on the forward and inverse transforms.          
    /// For size N=2^n transforms, the forward transform uses an            
    /// exp(B*2*pi/N) term and the inverse uses an exp(-B*2*pi/N) term.          
    /// Common values for (A,B) are                   
    ///  (0, 1) - default                    
    ///  (-1, 1) - data processing                  
    ///  (1,-1) - signal processing                  
    /// Abs(B) should be relatively prime to N.                
    /// Setting B=-1 effectively corresponds to conjugating both input and         
    /// output data.                       
    /// Usual values for B are 1 or -1.                  
    /// </summary>                       
    public int B { get; set; } 

    /// <summary>                        
    /// Scale data using n samples for forward and inverse transforms as needed        
    /// </summary>                       
    /// <param name="data"></param>                   
    /// <param name="n"></param>                    
    /// <param name="forward"></param>                  
    void Scale(double[] data, int n, bool forward)               
    {                           
     // forward scaling if needed                   
     if ((forward) && (A != 1))                   
     {                          
      var scale = Math.Pow(n, (A - 1)/2.0);               
      for (var i = 0; i < data.Length; ++i)                
       data[i] *= scale;                    
     }                          

     // inverse scaling if needed                   
     if ((!forward) && (A != -1))                   
     {                          
      var scale = Math.Pow(n, -(A + 1)/2.0);               
      for (var i = 0; i < data.Length; ++i)                
       data[i] *= scale;                    
     }                          
    }