Skip to content

Instantly share code, notes, and snippets.

@ksasao
Created August 16, 2016 23:02
Show Gist options
  • Save ksasao/c99dc644b221eb2752da5a1178b52790 to your computer and use it in GitHub Desktop.
Save ksasao/c99dc644b221eb2752da5a1178b52790 to your computer and use it in GitHub Desktop.
.wavファイルレンダリング
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WavPlayerTest
{
public class SortedItems<T>
{
private T[] _array;
public T this[int index] { get { return _array[index]; } set { _array[index] = value; } }
public int Length { get { return _array.Length; } }
public SortedItems()
{
_array = new T[0];
}
/// <summary>
/// 値を追加します
/// </summary>
/// <param name="item">追加する値</param>
public void Add(T item)
{
if(_array.Length == 0)
{
_array = new T[] { item };
return;
}
T[] tmp = new T[_array.Length + 1];
Array.Copy(_array, tmp, _array.Length);
tmp[tmp.Length - 1] = item;
Array.Sort(tmp);
_array = tmp;
}
/// <summary>
/// 値の配列を追加します
/// </summary>
/// <param name="items">値の配列</param>
public void AddRange(T[] items)
{
T[] tmp = new T[_array.Length + items.Length];
Array.Copy(_array, tmp, _array.Length);
Array.Copy(items, 0, tmp, _array.Length, items.Length);
Array.Sort(tmp);
_array = tmp;
}
/// <summary>
/// 指定した値を削除します
/// </summary>
/// <param name="item">削除対象の値</param>
/// <returns>削除したindex(削除対象が見つからない場合は-1)</returns>
public int Remove(T item)
{
int index = IndexOf(item);
if(index >= 0)
{
T[] tmp = new T[_array.Length - 1];
Array.Copy(_array, 0, tmp, 0, index);
Array.Copy(_array, index + 1, tmp, index, tmp.Length - index);
_array = tmp;
}
return index;
}
/// <summary>
/// 指定した位置の値を削除します
/// </summary>
/// <param name="index">削除対象のindex</param>
/// <returns>削除した値</returns>
public T RemoveAt(int index)
{
T result = _array[index];
T[] tmp = new T[_array.Length - 1];
Array.Copy(_array, 0, tmp, 0, index);
Array.Copy(_array, index + 1, tmp, index, tmp.Length - index);
_array = tmp;
return result;
}
/// <summary>
/// 値の位置を返します
/// </summary>
/// <param name="item">値</param>
/// <returns>最初に見つかった位置(見つからない場合は負の値)</returns>
public int IndexOf(T item)
{
return Array.BinarySearch<T>(_array, item);
}
/// <summary>
/// 値を配列に変換します。
/// </summary>
/// <returns>ソートされた値の配列</returns>
public T[] ToArray()
{
// コピーを返す
T[] tmp = new T[_array.Length];
Array.Copy(_array, tmp, _array.Length);
return tmp;
}
}
}
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;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using System.Threading;
using System.IO;
using System.Diagnostics;
namespace WavPlayerTest
{
public partial class WavPlayer : UserControl
{
private string _wavFile = "";
private string _markerFile = "";
private long _position;
private SortedItems<float> _marker = new SortedItems<float>();
bool isChanged = false;
/// <summary>
/// データサンプル数
/// </summary>
public long Samples { get; private set; }
/// <summary>
/// サンプリングレート(Hz)
/// </summary>
public int SampleRate { get; private set; }
public WavPlayer()
{
InitializeComponent();
InitializeParams();
}
public TimeSpan FromTime { get; private set; }
public TimeSpan ToTime { get; private set; }
private void InitializeParams()
{
_marker.Add(0.0f);
_marker.Add(-4.0f);
Random rnd = new Random();
for(int i = 0; i < 20; i++)
{
_marker.Add((float)rnd.NextDouble()*3);
}
var data = _marker.RemoveAt(10);
}
/// <summary>
/// 読み取り対象の .wav ファイルを指定します
/// </summary>
/// <param name="wavFile">.wavファイル名</param>
/// <param name="markerFile">時刻を列挙したファイル</param>
/// <returns>サンプル数</returns>
public long SetFiles(string wavFile, string markerFile)
{
_wavFile = wavFile;
_markerFile = markerFile;
using (WaveFileReader reader = new WaveFileReader(wavFile))
{
CheckFormat(reader);
_position = 0;
Samples = reader.Length / 2;
SampleRate = reader.WaveFormat.SampleRate;
hScrollBar1.Maximum = (int)Samples;
}
if (File.Exists(_markerFile))
{
string[] list = File.ReadAllLines(_markerFile);
float[] markers = (from c in list
select (float)Convert.ToDouble(c)).ToArray();
_marker.AddRange(markers);
}
SaveMarkerFile();
isChanged = true;
return Samples;
}
private void SaveMarkerFile()
{
string[] data = (from c in _marker.ToArray()
select c.ToString("0.0000")).ToArray();
File.WriteAllLines(_markerFile,data);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
DrawMain(pe);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Invalidate();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
Invalidate();
}
private void DrawMain(PaintEventArgs pe)
{
if (isChanged)
{
Graphics g = pe.Graphics;
Rectangle rect = pe.ClipRectangle;
SolidBrush b = new SolidBrush(this.ForeColor);
int width = rect.Width;
int height = rect.Height;
int scale = 240;
int channel = 0;
using (WaveFileReader reader = new WaveFileReader(_wavFile))
{
CheckFormat(reader);
int align = reader.WaveFormat.BlockAlign;
hScrollBar1.Maximum = (int)(reader.Length / scale / align);
int x = hScrollBar1.Value * scale;
int dataLength = width * align * scale;
byte[] data = new byte[dataLength];
int ms = (int)(1000L * x * align / (reader.WaveFormat.SampleRate * reader.WaveFormat.BlockAlign));
int me = (int)(1000L * (x * align + data.Length) / (reader.WaveFormat.SampleRate * reader.WaveFormat.BlockAlign));
FromTime = new TimeSpan(0, 0, 0, 0, ms);
ToTime = new TimeSpan(0, 0, 0, 0, me);
string time = FromTime.ToString(@"hh\:mm\:ss\.fff");
string time2 = ToTime.ToString(@"hh\:mm\:ss\.fff");
Console.WriteLine(time);
Console.WriteLine(time2);
Stopwatch watch = new Stopwatch();
watch.Start();
reader.Seek(channel * 2 + x * align, System.IO.SeekOrigin.Begin);
reader.Read(data, 0, data.Length);
Bitmap bmp = new Bitmap(width, height);
using (Graphics gg = Graphics.FromImage(bmp))
{
Pen pen = new Pen(b);
int p = 0;
float oldY0 = 0;
float oldY1 = 0;
for (int i = 0; i < width; i++)
{
int min = int.MaxValue;
int max = int.MinValue;
for (int j = 0; j < scale; j++)
{
int a = BitConverter.ToInt16(data, p);
p += align;
if (max < a) { max = a; }
if (a < min) { min = a; }
}
float y0 = height / 2 - (height * min / 65536f);
float y1 = height / 2 - (height * max / 65536f);
y1 = (y1 - y0 < 1) ? y1 + 0.5f : y1;
gg.DrawLine(pen, i, y0, i, y1);
if (i > 0)
{
if (y1 > oldY0)
{
gg.DrawLine(pen, i - 1, oldY0, i, y1);
}
if (y0 < oldY1)
{
gg.DrawLine(pen, i - 1, oldY1, i, y0);
}
}
oldY0 = y0;
oldY1 = y1;
}
pen.Dispose();
}
g.DrawImageUnscaled(bmp, rect);
bmp.Dispose();
watch.Stop();
Console.WriteLine("{0} ms", watch.ElapsedMilliseconds);
}
// カーソル位置に応じた描画
System.Drawing.Point sp = System.Windows.Forms.Cursor.Position;
System.Drawing.Point cp = this.PointToClient(sp);
g.DrawLine(Pens.White, 0, cp.Y, rect.Width-1, cp.Y);
g.DrawLine(Pens.White, cp.X, 0, cp.X, rect.Height-1);
b.Dispose();
Play();
}
}
WaveOut player = new WaveOut();
Object lockObject = new object();
public void Play()
{
if(_wavFile == "")
{
throw new NoNullAllowedException("SetWaveFileメソッドで.wavファイルを設定してください。");
}
Task task = new Task(() =>
{
using (var reader = new WaveFileReader(_wavFile))
{
long from = ((long) (FromTime.TotalSeconds * reader.WaveFormat.SampleRate)) * reader.WaveFormat.BlockAlign;
long len = (int)((ToTime.TotalSeconds - FromTime.TotalSeconds) * reader.WaveFormat.SampleRate) * reader.WaveFormat.BlockAlign;
byte[] data = new byte[len];
reader.Seek(from, System.IO.SeekOrigin.Begin);
reader.Read(data, 0, data.Length);
IWaveProvider provider = new RawSourceWaveStream(
new MemoryStream(data), reader.WaveFormat);
lock (lockObject)
{
if (player.PlaybackState != PlaybackState.Playing)
{
player.Stop();
}
while(player.PlaybackState != PlaybackState.Stopped)
{
Thread.Sleep(10);
}
player.Init(provider);
player.Play();
}
}
});
task.Start();
}
private static void CheckFormat(WaveFileReader reader)
{
if (reader.WaveFormat.Channels != 1 || reader.WaveFormat.BitsPerSample != 16)
{
throw new FormatException("16bitモノラルのデータではありません");
}
}
private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
Invalidate();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment