2015-11-01 22 views
-1

在我寫在高中的論文範圍內,我選擇從頭開始製作自己的音頻文件到光譜圖轉換器,以創建出這些光譜圖中的風景。如何擺脫譜圖中的垂直條紋?

我已經有我的FFT的實現和使用它來做一個高度圖,譜圖。但是當頻率變得密集時,我經常會以垂直條紋的形式出現奇怪的文物,如下圖所示。

Spectrogram of Mozart's Requiem, Lacrimosa

的示例是在權爲2048的窗口長度的開始和對數^ 2級。我使用的FFT是完美的,我已經將它與其他人進行了比較,他們產生了相同的結果。

這是變換幅度到頻率並將其存儲在一個二維數組功能:

private void transform(int from, int until) { 
    double val, step; 
    for(int i=from; i<until; i++) { 
     for(int j=0; j<n; j++) 
      chunk[j] = data[0][i*n+j+start]; 

     fft.realForward(chunk); 

     for(int j=0; j<height; j++) { 
      val = Math.sqrt(chunk[2*j]*chunk[2*j] + chunk[2*j+1]*chunk[2*j+1]); 
      map[i][j] = val; 
     } 
    } 
} 

現在我的問題:在哪裏這些垂直條紋來自,我如何擺脫他們?

我目前沒有使用窗口函數,每個計算都是相互串接的,這意味着沒有重疊。這是您可以考慮製作譜圖的最簡單方法。它可以幫助引入一個窗口函數或做每一個計算獨立的框架是否已經參與了以前的計算,也就是說重疊框架窗口

另外,還有什麼其他方法可以改進我的基本方法以獲得更好的結果?

這是全班同學。我給它的數據和所有必要的信息從音頻文件:

import java.awt.*; 
import java.awt.event.*; 
import java.awt.image.*; 
import java.io.*; 

import javax.imageio.ImageIO; 
import javax.swing.*; 

import org.jtransforms.fft.DoubleFFT_1D; 

public class Heightmap extends JFrame implements WindowListener{ 
    public static final int LOG_SCALE = 0; 
    public static final int LOG_SQUARE_SCALE = 1; 
    public static final int SQUARE_SCALE = 2; 
    public static final int LINEAR_SCALE = 3; 

    private BufferedImage heightmap; 
    private FileDialog chooser; 

    private JMenuBar menuBar; 
    private JMenu fileMenu; 
    private JMenuItem save, close; 

    private DoubleFFT_1D fft; 
    private int[][] data; 
    private double[][] map; 
    private double[] chunk; 
    private int width, height, n, start, scale; 
    private String name; 

    private boolean inactive; 

    public Heightmap(int[][] data, int resolution, int start, 
      int width, int height, int scale, String name) { 

     this.data = data; 
     this.n = resolution; 
     this.start = start; 
     this.width = width; 
     this.height = height; 
     this.scale = scale; 
     this.name = name; 

     fft = new DoubleFFT_1D(n); 
     map = new double[width][height]; 
     heightmap = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
     chunk = new double[n];  

     System.out.println("Starting transformation..."); 

     long time; 
     time = System.currentTimeMillis(); 
     transform(); 
     time = System.currentTimeMillis() - time; 
     System.out.println("Time taken for calculation: "+time+" ms"); 

     time = System.currentTimeMillis(); 
     makeHeightmap(); 
     initComponents(); 
     time = System.currentTimeMillis() - time; 
     System.out.println("Time taken for drawing heightmap: "+time+" ms"); 

    } 

    private void initComponents() { 
     this.setSize(width, height); 
     this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
     this.setResizable(false); 
     this.setLocationRelativeTo(null); 
     this.setTitle(name); 

     createMenuBar(); 
     chooser = new FileDialog(this, "Choose a directory", FileDialog.SAVE); 
     chooser.setDirectory("/Users/<user>/Desktop"); 

     this.addMouseListener(new HeightmapMouseListener()); 
     this.addKeyListener(new HeightmapKeyListener()); 
     this.addWindowListener(this); 

     this.setVisible(true); 

    } 

    private void createMenuBar() { 
     menuBar = new JMenuBar(); 
     fileMenu = new JMenu(); 

     fileMenu.setText("File"); 

     save = new JMenuItem("Save...", KeyEvent.VK_S); 
     save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.META_DOWN_MASK)); 
     save.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent arg0) { 
       chooser.setVisible(true); 

       String fileName = chooser.getFile(); 
       String dir = chooser.getDirectory(); 
       chooser.setDirectory(dir); 

       if(fileName != null) { 
        try { 
         File outputfile = new File(dir + fileName + ".png"); 
         ImageIO.write(heightmap, "png", outputfile); 
        } catch (IOException e) { 
         e.printStackTrace(); 
        } 
        System.out.println("Saved "+fileName+".png to "+dir); 
       } 
      } 
     }); 

     close = new JMenuItem("Close", KeyEvent.VK_C); 
     close.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.META_DOWN_MASK)); 
     close.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       setVisible(false); 
       dispose(); 
      } 
     }); 

     fileMenu.add(save); 
     fileMenu.addSeparator(); 
     fileMenu.add(close); 

     menuBar.add(fileMenu); 
     this.setJMenuBar(menuBar); 
    } 

    public void paint(Graphics g) { 
     g.drawImage(heightmap, 0, 0, null); 
    } 

    private void transform() { 
     transform(0, width); 
    } 
    private void transform(int from, int until) { 
     double max = Double.MIN_VALUE; 
     double min = Double.MAX_VALUE; 
     double val, step; 
     for(int i=from; i<until; i++) { 
      for(int j=0; j<n; j++) { 
       chunk[j] = data[0][i*n+j+start]; 
      } 
      fft.realForward(chunk); 

      for(int j=0; j<height; j++) { 
       val = Math.sqrt(chunk[2*j]*chunk[2*j] + chunk[2*j+1]*chunk[2*j+1]); 

       if(val > max) 
        max = val; 
       if(val < min) 
        min = val; 
       map[i][j] = val; 
      } 

      if(min != 0) { 
       step = max/(max-min); 
       for(int j=0; j<height; j++) 
        map[i][j] = (map[i][j]-min)*step; 
      } 
     } 
    } 

    /* 
    * Paints heightmap into the BufferedImage 
    */ 
    private void makeHeightmap() { 
     double max = 0; 
     switch(scale) { 
     case LOG_SCALE: max = Math.log(findMax(map)+1); break; 
     case LOG_SQUARE_SCALE: max = Math.pow(Math.log(findMax(map)+1), 2); break; 
     case SQUARE_SCALE: max = Math.sqrt(findMax(map)); break; 
     case LINEAR_SCALE: max = findMax(map); break; 
     default: max = Math.pow(Math.log(findMax(map)+1), 2); break; 
     } 
     double stepsize = 255.0/max; 
     int val, rgb; 

     for(int x=0; x<width; x++) 
      for(int y=0; y<height; y++) { 
       switch(scale) { 
       case LOG_SCALE: val = (int) (Math.log(map[x][y]+1)*stepsize); break; 
       case LOG_SQUARE_SCALE: val = (int) (Math.log(map[x][y]+1)*stepsize); val *= val; break; 
       case SQUARE_SCALE: val = (int) (Math.sqrt(map[x][y])*stepsize); break; 
       case LINEAR_SCALE: val = (int) (map[x][y]*stepsize); break; 
       default: val = (int) (Math.log(map[x][y]+1)*stepsize); val *= val; break; 
       } 
       rgb = 255<<24 | val<<16 | val<<8 | val; 
       heightmap.setRGB(x, height-y-1, rgb); 
      } 

    } 

    private double findMax(double[][] data) { 
     double max = 0; 
     for(double[] val1: data) 
      for(double d: val1) 
       if(d > max) 
        max = d; 
     return max; 
    } 

    private class HeightmapKeyListener implements KeyListener { 
     boolean busy = false; 

     public void keyPressed(KeyEvent e) { 

      if(e.getKeyCode() == KeyEvent.VK_RIGHT && !busy && start < data[0].length-width*n) { 
       busy = true; 

       for(int x=0; x<width-1; x++) 
        map[x] = map[x+1].clone(); 

       start += n; 
       transform(width-1, width);    
       makeHeightmap(); 
       repaint(); 
       busy = false; 
      } 
      else if(e.getKeyCode() == KeyEvent.VK_LEFT && !busy && start > 0) { 
       busy = true; 

       for(int x=width-1; x>0; x--) 
        map[x] = map[x-1]; 

       start -= n; 
       transform(0, 1); 
       makeHeightmap(); 
       repaint(); 
       busy = false; 
      } 
     } 

     public void keyReleased(KeyEvent e) { } 
     public void keyTyped(KeyEvent e) { } 
    } 

    private class HeightmapMouseListener implements MouseListener { 


     public void mouseClicked(MouseEvent e) { 
      if(inactive) { 
       inactive = false; 
       return; 
      } 

      long time = System.currentTimeMillis(); 

      int posX = e.getX(); 
      int diff = posX - width/2; //difference between old and new center in pixels 
      int oldStart = start; 

      start = start + diff*n; 
      if(start < 0) start = 0; 
      int maxFrame = data[0].length-width*n; 
      if(start > maxFrame) start = maxFrame; 
      if(start == oldStart) return; 

      System.out.println("Changing center..."); 

      int absDiff = Math.abs(diff); 
      if(start < oldStart) { //shift the start backward, recalculate the start 
       for(int x=width-1; x>=absDiff; x--) 
        map[x] = map[x-absDiff].clone(); 
       transform(0, absDiff); 
      } 
      else if(start > oldStart) { //shift the back forward, recalculate the back 
       for(int x=0; x<width-absDiff; x++) 
        map[x] = map[x+absDiff].clone(); 
       transform(width-absDiff, width); 
      } 

      makeHeightmap(); 
      repaint(); 
      System.out.println("Time taken: "+(System.currentTimeMillis()-time)+" ms"); 

     } 

     public void mousePressed(MouseEvent e) { } 
     public void mouseReleased(MouseEvent e) { } 
     public void mouseEntered(MouseEvent e) { } 
     public void mouseExited(MouseEvent e) { } 
    } 

    public void windowActivated(WindowEvent arg0) { } 
    public void windowClosed(WindowEvent arg0) { } 
    public void windowClosing(WindowEvent arg0) { } 
    public void windowDeactivated(WindowEvent arg0) { 
     inactive = true; 
    } 
    public void windowDeiconified(WindowEvent arg0) { } 
    public void windowIconified(WindowEvent arg0) { } 
    public void windowOpened(WindowEvent arg0) { } 

} 

編輯: 實現窗函數顯着提高的結果。我真的不明白窗口函數會做什麼,因此低估了它的效果。

但是,這樣做之後,我試圖映射一個餘弦波與(再)產生了一些奇怪的文物爲10kHz的頻率: enter image description here

什麼能這一塊的原因是什麼?我通過將0到0之間的所有內容以及255到255之間的所有內容進行了限制,而無需任何更改。

+3

沒有代碼看,很難說出什麼可能是錯的。你能發表一個演示這種行爲的實例嗎?它可能有助於追蹤問題。 – Tunaki

+0

請添加您用於生成圖像的代碼。也許增加一些FFT輸入/輸出的例子(你說它的工作完美無缺,我們不確定,因爲你確定)。最後,通過將FFT轉換回正在調查的音頻來測試FFT會更好。它應該是一樣的。如果不是,那麼你的FFT就是問題所在。 – 2015-11-01 21:53:21

+0

我突出顯示了實際進行轉換的代碼,並添加了整個類。我爲那些混亂且評論不好的代碼表示歉意。我懷疑代碼本身有什麼問題,而是用我的數據計算方法。 – Metaphalo

回答

1

這種類型的工件可能是由於溢出或以其他方式超過了顏色映射函數之前或之中的參數邊界,或者可能是由於某些函數(log?)返回了NaN值。您可以通過輸入一些用於超出範圍或非法值的斷言來找到它。

+0

我試着簡單地將顏色值剪裁爲0或255,而不做任何改變。雖然應用窗口當然有很大的幫助,但仍然有一些遺留物,我不知道它們來自哪裏。 – Metaphalo

相關問題