フーリエ変換では、信号すべての周波数特性を得ます。時系列での周波数特性をみるのは得意ではありません。
そこで、今回は時間ごとの周波数特性をみるためにウェーブレット変換をやってみました。
赤いのが強い部分、青いのが弱い部分で、上にいくほど周波数が高くなります。
ウェーブレット変換では、前後を絞った正弦波など解析の種になる波を使って、周波数ごと時間ごとの特性を解析していきます。
このような、解析につかう波を、マザーウェーブレットといいます。ウェーブレット解析では、周波数ごとに絞りの長さや振幅を変えて、マザーウェーブレットをずらしながら時間ごとに解析していきます。
今回はマザーウェーブレットとして、ガボールのマザーウェーブレットと呼ばれるものを使いました。
解析する信号は、MP3ファイルを読み込んで、4秒後からのデータを使います。
//Wavelet.java import java.awt.*; import java.awt.geom.GeneralPath; import java.awt.image.BufferedImage; import java.io.InputStream; import javax.sound.sampled.*; import javax.swing.*; import static java.lang.Math.*; public class Wavelet { public static void main(String[] args) throws Exception{ //MP3読み込みの準備 InputStream is = Mp3.class.getResourceAsStream("/test.mp3"); AudioInputStream in = null; AudioInputStream din = null; in = AudioSystem.getAudioInputStream(is); AudioFormat format = in.getFormat(); AudioFormat decodedFormat = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), 16, format.getChannels(), format.getChannels() * 2, format.getSampleRate(), false); din = AudioSystem.getAudioInputStream(decodedFormat, in); double[] data = new double[512]; byte[] buf = new byte[4]; //4秒飛ばす for(int i = 0; i < format.getSampleRate() * 4 && din.read(buf, 0, buf.length) != -1; ++i){} //データ読み込み double max = 0; for(int i = 0; i < data.length && din.read(buf, 0, buf.length) != -1; ++i){ int left = buf[1] * 256 + buf[0]; //int right = buf[3] * 256 + buf[2];//今回は使わない data[i] = left / 32768.; if(max < abs(data[i])) max = abs(data[i]); } //最大値で正規化 for(int i = 0; i < data.length; ++i){ data[i] /= max; } double[][] p = new double[25][data.length]; max = 0; for(int fi = 0; fi < p.length; ++fi){ double a = data.length / pow(2, fi / 3.);//1/3オクターブごと //ガボールのウェーブレットを作成 double sigma = 2; double winrate = 0.01; int offset = (int)(sqrt(-2 * sigma * sigma * a * a * log(winrate))); int gaborlen = offset * 2 + 1; double[] gaborre = new double[gaborlen]; double[] gaborim = new double[gaborlen]; for(int i = 0; i < gaborlen; ++i){ double x = (i - offset) / a; double d = exp(-x * x / (2 * sigma * sigma)) / sqrt(2 * PI * sigma * sigma); gaborre[i] = d * cos(PI * x) / sqrt(a); gaborim[i] = d * sin(PI * x) / sqrt(a); } //解析 for(int i = 0; i < data.length; ++i){ double re = 0; double im = 0; for(int gb = 0; gb < gaborlen; ++gb){ int gg = gb - gaborlen / 2 + i; double d = (gg < 0 || gg >= data.length) ? 0 : data[gg]; re += gaborre[gb] * d; im += gaborim[gb] * d; } p[fi][i] = (sqrt(re * re + im * im)); if(p[fi][i] > max) max = p[fi][i]; } } //描画 BufferedImage img = new BufferedImage(400, 300, BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D) img.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, 400, 300); g.setColor(Color.BLACK); //元データ GeneralPath gp = new GeneralPath(); gp.moveTo(30, 50); for(int i = 0; i < data.length; ++i){ int x = i * 5 / 8 + 30; gp.lineTo(x, -data[i] * 40 + 50); } g.draw(gp); //周波数分布の描画 for(int fi = 0; fi < p.length; ++fi){ int y = 300 - fi * 200 / p.length; for(int i = 0; i < p[fi].length; ++i){ float d = (float) (p[fi][i] / max); float r = 1 - d; float gr = (d < .5) ? d * 2 : (1 - d) * 2; float b = d; g.setColor(new Color(b, gr, r)); int x = i * 5 / 8 + 30; g.fillRect(x, y, 1, 8); } } //表示 JFrame f = new JFrame("ウェーブレット変換"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(400, 350); f.add(new JLabel(new ImageIcon(img))); f.setVisible(true); } }