using NAudio.Dsp;
using NAudio.Wave;
using System;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
namespace Tutorial14
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openfiledlg = new OpenFileDialog();
if (openfiledlg.ShowDialog() != DialogResult.OK) return;
string fileName = openfiledlg.FileName;
//TODO: 関数化
AudioFileReader reader = new AudioFileReader(fileName);
int FS = reader.WaveFormat.SampleRate;
float[] samples = new float[reader.Length / reader.BlockAlign * reader.WaveFormat.Channels];
reader.Read(samples, 0, samples.Length); // Wavのデータを配列(samples)にコピー
int fftLength = 512;
int m = (int)Math.Log(fftLength, 2.0);
int fftPos = 0;
Console.WriteLine("FFT START" + Environment.NewLine);
Complex[] buffer = new Complex[fftLength];
float[,] result = new float[samples.Length / fftLength, fftLength / 2];
for (int i = 0; i < samples.Length; i++)
{
// FastFourierTransform.FFTに突っ込むためのComplex配列
buffer[fftPos].X = (float)(samples[i] * FastFourierTransform.HammingWindow(fftPos, fftLength));
buffer[fftPos].Y = 0.0f;
// フレームシフト
fftPos++;
if (fftLength <= fftPos)
{
fftPos = 0;
FastFourierTransform.FFT(true, m, buffer);
for (int k = 0; k < fftLength / 2; k++)
{
double diagonal = Math.Sqrt(buffer[k].X * buffer[k].X + buffer[k].Y * buffer[k].Y);
double intensityDB = 10.0 * Math.Log10(diagonal);
const double minDB = -120.0;
double percent = (intensityDB < minDB) ? 0.0 : 1 - (intensityDB / minDB);
result[i / fftLength, k] = (float)percent;
}
}
}
Console.WriteLine("FFT End" + Environment.NewLine);
this.contourMap1.plot(result);
/**********************************************************************************************/
// 波形の表示
/**********************************************************************************************/
Series waveChart = new Series();
//グラフのタイプを指定(今回は線)
waveChart.ChartType = SeriesChartType.Line;
//グラフのデータを追加(試しにsin関数)
for (int i = 0; i < samples.Length / chart1.Width; i++)
waveChart.Points.AddXY(i * chart1.Width * (1 / (double)FS), samples[i * chart1.Width]);
//作ったSeriesをchartコントロールに追加する
chart1.Series.Add(waveChart);
/**********************************************************************************************/
// フラクタルの表示
/**********************************************************************************************/
int FractalWindow = 1024;
int[] taus = new int[] { 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
int Ntaus = taus.Length;
int tau = taus[0];
int tauMax = taus[Ntaus - 1];
float[] fractalNumbers = new float[samples.Length];
int pos = 0;
float fractal_n = 0;
for (int i = 0; i < samples.Length; i++)
{
if (i > FractalWindow + tauMax)
{
fractal_n = 0;
for (int j = 0; j < FractalWindow; j++)
{
fractal_n += (float)Math.Pow(samples[i-j] - samples[i - j - tauMax], 2) / FractalWindow;
}
fractalNumbers[i] = fractal_n;
fractal_n = 0;
}
else
{
fractalNumbers[i] = 0;
}
}
// 波形の表示
Series fractalChart = new Series();
//グラフのタイプを指定(今回は線)
fractalChart.ChartType = SeriesChartType.Line;
//グラフのデータを追加(試しにsin関数)
for (int i = 0; i < samples.Length / chart2.Width; i++)
fractalChart.Points.AddXY(i * chart2.Width * (1 / (double)FS), fractalNumbers[i * chart2.Width]);
//作ったSeriesをchartコントロールに追加する
chart2.Series.Add(fractalChart);
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tutorial14
{
public partial class ContourMap : UserControl
{
// スペクトログラムのBitmapばっふぁ
Bitmap bitmap_buffer = null;
public ContourMap()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.pictureBox1.Paint += pictureBox1_Paint;
}
/// <summary>
/// プロットデータをセット
/// </summary>
public void plot(float[,] listBuffer)
{
int height = this.pictureBox1.Height;
int width = this.pictureBox1.Width;
int NumFrame = listBuffer.GetLength(0);
int NumBin = listBuffer.GetLength(1);
Console.WriteLine($"NumFrame:{NumFrame}, NumBin:{NumBin} ");
bitmap_buffer = new Bitmap(width, height);
Color[] cmap = Contour.Create();
for (int k = 0; k < height; k++)
{
for (int j = 0; j < width; j++)
{
////色を決める
int index = (int)(NumBin * (float)k / height);
int frame = (int)(NumFrame * (float)j / width);
if (index > NumBin - 1) throw new Exception();
if (frame > NumFrame - 1) throw new Exception();
float value = listBuffer[frame, index];
if (value > 1.0) value = 1.0F;
else if (value < 0.0) value = 0.0F;
int cindex = (int)(255.0F * value);
if (cindex > 255) throw new Exception();
Color c = cmap[cindex];
////1つのピクセルの色を変える
//bitmap_buffer.SetPixel(j, height - k - 1, c);
bitmap_buffer.SetPixel(j, (height-1)-k, c);
}
}
//補間方法を指定して画像を縮小して描画する
this.Refresh();
Console.WriteLine("plot end");
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (bitmap_buffer == null) return;
// Graphicsオブジェクトを作成する
var g = e.Graphics;
//補間方法として最近傍補間を指定する
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
////補間方法として高品質双三次補間を指定する
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
//画像を縮小して描画する
g.DrawImage(bitmap_buffer, 0, 0, pictureBox1.Width, pictureBox1.Height);
}
protected override void OnResize(EventArgs e)
{
this.Refresh();
base.OnResize(e);
}
}
public class Contour
{
public static Color[] Create()
{
Color[] colors = new Color[256];
for (int i = 0; i < 256; i++)
{
float[] rgb = Contour.ColorScaleBCGYR(i/(float)255);
colors[i] = Color.FromArgb((int)(255 * rgb[0]), (int)(255 * rgb[1]), (int)(255 * rgb[2]));
}
return colors;
}
public static float[] ColorScaleBCGYR(double in_value)
{
// 0.0~1.0 の範囲の値をサーモグラフィみたいな色にする
// 0.0 1.0
// 青 水 緑 黄 赤
// 最小値以下 = 青
// 最大値以上 = 赤
float[] ret;
int a = 255; // alpha値
int r, g, b; // RGB値
double value = in_value;
double tmp_val = Math.Cos(4 * Math.PI * value);
int col_val = (int)((-tmp_val / 2 + 0.5) * 255);
if (value >= (4.0 / 4.0)) { r = 255; g = 0; b = 0; } // 赤
else if (value >= (3.0 / 4.0)) { r = 255; g = col_val; b = 0; } // 黄~赤
else if (value >= (2.0 / 4.0)) { r = col_val; g = 255; b = 0; } // 緑~黄
else if (value >= (1.0 / 4.0)) { r = 0; g = 255; b = col_val; } // 水~緑
else if (value >= (0.0 / 4.0)) { r = 0; g = col_val; b = 255; } // 青~水
else { r = 0; g = 0; b = 255; } // 青
ret = new float[3] { (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f };
return ret;
}
}
}