NAudioで信号処理 (その1)
チュートリアル その1
NAudioを使ってWaveファイルの再生
C# Audio Tutorial 1 - Wave File with NAudio
肝は、WAVファイルをロードして、DIrectSoundで再生する。
wave = new NAudio.Wave.WaveFileReader(open.FileName); output = new NAudio.Wave.DirectSoundOut(); output.Init(new NAudio.Wave.WaveChannel32(wave)); output.Play();
メインのソースコードは以下のようになる。WAVの再生には、WAVファイルの選択->WAVのReaderの設定->出力先を指定->再生開始。のほかにも再生中断/再開や、再生が停止されたときのDispose処理が関わってくる。このチュートリアル(その1)では基本的なWAVファイル再生をベースとして、周辺のプレ処理、クロージング処理を実装しているので注目して欲しい。
using System; using System.Windows.Forms; namespace Tutorial1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { button_PauseWav.Enabled = false; } private NAudio.Wave.WaveFileReader wave = null; private NAudio.Wave.DirectSoundOut output = null; private void button_OpenWav_Click(object sender, EventArgs e) { OpenFileDialog open = new OpenFileDialog(); open.Filter = "Wave File (*.wav,*.WAV) |*.wav;*.WAV;|All files (*.*)|*.*"; if (open.ShowDialog() != DialogResult.OK) return; DisposeWave(); wave = new NAudio.Wave.WaveFileReader(open.FileName); output = new NAudio.Wave.DirectSoundOut(); output.Init(new NAudio.Wave.WaveChannel32(wave)); output.Play(); button_PauseWav.Enabled = true; } private void button_PauseWav_Click(object sender, EventArgs e) { if (output != null) { if (output.PlaybackState == NAudio.Wave.PlaybackState.Playing) output.Pause(); else if (output.PlaybackState == NAudio.Wave.PlaybackState.Paused) output.Play(); } } // Wave関連のインスタンスをDispose private void DisposeWave() { if (output != null) { if (output.PlaybackState == NAudio.Wave.PlaybackState.Playing) output.Stop(); output.Dispose(); output = null; } if (wave != null) { wave.Dispose(); wave = null; } } // フォームが閉じられたとき、Wave関連のインスタンスをDispose private void Form1_FormClosing(object sender, FormClosingEventArgs e) { this.DisposeWave(); } } }
NAudioで信号処理 (その11)
NAudioで信号処理 (目次) - Hope is a Dream. Dream is a Hope.
NAudioで信号処理 2 (その11)
NAudio.Gui.WaveViewerをカスタムしてグラフ描画!
いよいよグラフ描画です。信号処理はグラフを描画することから始まります。チュートリアルでは独自グラフの実装ではなく、chartクラスと、NAudioのWaveViewerクラスを使っての実装となります。
CustomWaveViewer
NAudio.Gui.WaveViewerをコピペして、ズームやらフィットやらをカスタム
using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; using NAudio.Wave; namespace Tutorial { /// <summary> /// Control for viewing waveforms /// </summary> public class CustomWaveViewer : System.Windows.Forms.UserControl { #region プロパティ public Color PenColor { get; set; } public float PenWidth { get; set; } #endregion public void FitToScreen() { if (waveStream == null) return; int samples = (int)(waveStream.Length / bytesPerSample); this.startPosition = 0; this.SamplesPerPixel = samples / this.Width; } public void Zoom(int leftSample, int rightSample) { this.startPosition = leftSample * bytesPerSample; // [byte] this.SamplesPerPixel = (rightSample - leftSample) / this.Width; // [sample/pixel] } #region Mouse private Point mousePos, startPos; private bool mouseDrag = false; protected override void OnMouseDown(MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) { startPos = e.Location; mousePos = new Point(-1, 1); mouseDrag = true; DrawVerticalLine(e.X); } base.OnMouseDown(e); } protected override void OnMouseMove(MouseEventArgs e) { if (mouseDrag) { DrawVerticalLine(e.X); // マウス位置にライン描画 if (mousePos.X != -1) DrawVerticalLine(mousePos.X); //前のラインを消す mousePos = e.Location; } base.OnMouseMove(e); } protected override void OnMouseUp(MouseEventArgs e) { if (mouseDrag && e.Button == System.Windows.Forms.MouseButtons.Left) { mouseDrag = false; DrawVerticalLine(startPos.X); if (mousePos.X == -1) return; DrawVerticalLine(mousePos.X); int leftSample = (int)(StartPosition / bytesPerSample + SamplesPerPixel * Math.Min(startPos.X, mousePos.X)); int rightSample = (int)(StartPosition / bytesPerSample + SamplesPerPixel * Math.Max(startPos.X, mousePos.X)); Zoom(leftSample, rightSample); }else if(e.Button == MouseButtons.Middle){ this.FitToScreen(); } base.OnMouseUp(e); } #endregion private void DrawVerticalLine(int x) { ControlPaint.DrawReversibleLine( PointToScreen(new Point(x, 0)), PointToScreen(new Point(x, Height)), Color.Black); } protected override void OnResize(EventArgs e) { this.FitToScreen(); base.OnResize(e); } /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; private WaveStream waveStream; private int samplesPerPixel = 128; private long startPosition; private int bytesPerSample; /// <summary> /// Creates a new WaveViewer control /// </summary> public CustomWaveViewer() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); this.DoubleBuffered = true; this.PenColor = Color.DodgerBlue; this.PenWidth = 1; } /// <summary> /// sets the associated wavestream /// </summary> public WaveStream WaveStream { get { return waveStream; } set { waveStream = value; if (waveStream != null) { bytesPerSample = (waveStream.WaveFormat.BitsPerSample / 8) * waveStream.WaveFormat.Channels; } this.Invalidate(); } } /// <summary> /// The zoom level, in samples per pixel /// </summary> public int SamplesPerPixel { get { return samplesPerPixel; } set { samplesPerPixel = Math.Max(1, value); this.Invalidate(); } } /// <summary> /// Start position (currently in bytes) /// </summary> public long StartPosition { get { return startPosition; } set { startPosition = value; } } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } /// <summary> /// <see cref="Control.OnPaint"/> /// </summary> protected override void OnPaint(PaintEventArgs e) { if (waveStream != null) { waveStream.Position = 0; int bytesRead; byte[] waveData = new byte[samplesPerPixel * bytesPerSample]; waveStream.Position = startPosition + (e.ClipRectangle.Left * bytesPerSample * samplesPerPixel); using (Pen linePen = new Pen(PenColor, PenWidth)) { for (float x = e.ClipRectangle.X; x < e.ClipRectangle.Right; x += 1) { short low = 0; short high = 0; bytesRead = waveStream.Read(waveData, 0, samplesPerPixel * bytesPerSample); if (bytesRead == 0) break; for (int n = 0; n < bytesRead; n += 2) { short sample = BitConverter.ToInt16(waveData, n); if (sample < low) low = sample; if (sample > high) high = sample; } float lowPercent = ((((float)low) - short.MinValue) / ushort.MaxValue); float highPercent = ((((float)high) - short.MinValue) / ushort.MaxValue); e.Graphics.DrawLine(linePen, x, this.Height * lowPercent, x, this.Height * highPercent); } } } base.OnPaint(e); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } }
NAudioで信号処理 (その10)
NAudioで信号処理 (目次) - Hope is a Dream. Dream is a Hope.
NAudioで信号処理 (その10)
グラフ描画!
C# Audio Tutorial 10 - Plotting Audio Waveforms
いよいよグラフ描画です。信号処理はグラフを描画することから始まります。チュートリアルでは独自グラフの実装ではなく、chartクラスと、NAudioのWaveViewerクラスを使っての実装となります。
using System; using System.Windows.Forms; using NAudio.Wave; using System.Drawing; namespace Tutorial { public partial class Form1 : Form { #region form public Form1() { InitializeComponent(); } private void openToolStripMenuItem_Click(object sender, EventArgs e) { // WAV File Open OpenFileDialog open = new OpenFileDialog(); open.Filter = "WAV File (*.wav)|*.wav;"; if (open.ShowDialog() != DialogResult.OK) return; // NAudio WaveViewerのセットアップ waveViewer1.BackColor = Color.White; waveViewer1.SamplesPerPixel = 400; waveViewer1.StartPosition = 40000; waveViewer1.WaveStream = new WaveFileReader(open.FileName); // ストリームを指定するだけでよい // 以下、chartのセットアップ chart1.Series.Add("wave"); chart1.Series["wave"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine; chart1.Series["wave"].ChartArea = "ChartArea1"; WaveChannel32 wave = new WaveChannel32(new WaveFileReader(open.FileName)); byte[] buffer = new byte[16384]; int read = 0; while(wave.Position < wave.Length) { read = wave.Read(buffer, 0, 16384); for (int i = 0; i < read/4; i++) { chart1.Series["wave"].Points.Add(BitConverter.ToSingle(buffer, i * 4)); } } } } }