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)); } } } } }