How to Draw Waveform While Recording Using NAudio and WPF
来源:互联网 发布:mac os maven环境变量 编辑:程序博客网 时间:2024/05/18 00:06
How to Draw Waveform While Recording Using NAudio and WPF
NAudio is a wonderful library that provides basic functionalities for processing audio files in wav or mp3 format. AndWPF is a hot technique for developing desktop application on Winodws. Combining both of them, we can develop a application that draw waveforms onCanvas while recording sound.
For detailed introduction to NAudio and WPF canvas, you can read the following articles written by me before.
http://www.assembleforce.com/category/csharp/audio-image
Several Ways to Draw While Recording
There are some strategies that can be used to draw curve of waves on Canvas while recording sounds and I will discuss those strategies and then choose one to implement.
The recording process involves several steps, they are:
- Register EventHandler for recording process, including DataAvailable handler, which record the data while input device raise the signal for buffering and RecordingStopped handler, which clean the environment and release resource after recoding is stopped by explicitly calling WaveIn.StopRecording() or buffer overflow while setting StopOnBufferOverflow to true.
//Assuming that we already had instance of WaveInwi.DataAvailable += new EventHandler(wi_DataAvailable);wi.RecordingStopped += new EventHandler(wi_RecordingStopped);
- Decide the WaveFormat of the input device should follow by assigning WavaIn.WaveFormat property the new WaveFormat instance.
wi.WaveFormat = new WaveFormat(44100, 32, 2);
- Start recording by invoking WaveIn.StartRecording().
wi.StartRecording();
What might be flexible happens inside the EventHandlers. So following the framework above, I will show you some different way for drawing curves while recording.
Strategy#1. Draw Curve Inside the DataAvailable EventHandler
Since we need to draw the waveform curve along with the recording process, we need to find a place that will be touched each time new recording data ia available. So the event handler is the first choice. Using this strategy, we can not control the times that canvas gets updated per second because the invocation of event handler is uncertain for us. However, it is ensured that as new data is available, we will get notified and then we can decide whether we update the canvas. So using this approach, we can respond to the data effectively.
Strategy#2. Write Data into a Buffer and Set a Timer to Update Canvas
The most important factor to use this approach it the decomposition of sampling data and drawing data operations. Sometime, we cannot update the UI element ouside in another thread, this approach gives light to this situation. However, additional synchronization is needed to ensure the consistency of data. Hence it makes the implementation complicated.
Strategy#3. Another Event Handler to Draw Curve
Actually, I did not try this approach yet, but if two invocation of event handlers share the same data, it shoudl work. It does not solve of problem when UI element cannot be updated in another thread, but it makes the sampling and transfering samples more efficient without being intterupted by additional draw.
Strategy to Display the Curve
There are two ways to display the curve, which are easy to understand. First one is to draw curve from left to right. If it hit the right most side, it goes back to the ledt side and then start to draw from left to right again. The second one is called moving windows. Thnking that I store all the samples in a list, then I define a window inside which the data will be displayed on the Canvas. The window will move as more data is sampled and buffered.
I prefer the second choice because it has a more friendly user experience.
The Result
I write some codes to implement such an application. It will start recording once the window is initialized. Then it draws the curve from left to rigth. Once it hit the right most side, all the curve will be push from the right to the left (looks like that) and new curve will be drawn upon the right border and then moved towards the left.
Conclusion
In this article, I show how to draw the wave form onthe Canvs using NAudio and WPF technique. This application is of particularity because it can be seen from most of music players. And is you want to develop a voice sampling application, this technique can also be used to enhance the usablity.
If you like this article, please press the retweet buttons.
Appendice: The Complete Code
I have tested the code so you can try it out by yourself. I sould also work on your computer.
using NAudio;using NAudio.Wave;
namespace WPFs{ /// <summary> /// waveShow.xaml 的交互逻辑 /// </summary> ///
public partial class waveShow : Window { public waveShow() { InitializeComponent();
StartRecording(50); } WaveIn wi; WaveFileWriter wfw; Polyline pl;
double canH = 0; double canW = 0; double plH = 0; double plW = 0; int time = 0; double seconds = 0;
List<byte> totalbytes; Queue<Point> displaypts; //Queue<short> displaysht; Queue<Int32> displaysht;
long count = 0; int numtodisplay = 2205; //sample 1/100, display for 5 seconds
void StartRecording(int time) { wi = new WaveIn(); wi.DataAvailable += new EventHandler<WaveInEventArgs>(wi_DataAvailable); wi.RecordingStopped += new EventHandler(wi_RecordingStopped); wi.WaveFormat = new WaveFormat(44100, 32, 2);
wfw = new WaveFileWriter("E:\\labdata\\record.wav", wi.WaveFormat);
canH = waveCanvas.Height; canW = waveCanvas.Width;
pl = new Polyline(); pl.Stroke = Brushes.Blue; pl.Name = "waveform"; pl.StrokeThickness = 1; pl.MaxHeight = canH - 4; pl.MaxWidth = canW - 4;
plH = pl.MaxHeight; plW = pl.MaxWidth;
this.time = time;
displaypts = new Queue<Point>(); totalbytes = new List<byte>(); //displaysht = new Queue<short>(); displaysht = new Queue<Int32>();
wi.StartRecording(); }
void wi_RecordingStopped(object sender, EventArgs e) { wi.Dispose(); wi = null; wfw.Close(); wfw.Dispose();
wfw = null; }
void wi_DataAvailable(object sender, WaveInEventArgs e) { seconds += (double)(1.0*e.BytesRecorded / wi.WaveFormat.AverageBytesPerSecond*1.0); if (seconds > time) { wi.StopRecording(); }
wfw.Write(e.Buffer, 0, e.BytesRecorded); totalbytes.AddRange(e.Buffer);
//byte[] shts = new byte[2]; byte[] shts = new byte[4];
for (int i = 0; i < e.BytesRecorded-1; i += 100) { shts[0] = e.Buffer[i]; shts[1] = e.Buffer[i + 1]; shts[2] = e.Buffer[i + 2]; shts[3] = e.Buffer[i + 3]; if (count < numtodisplay) { displaysht.Enqueue(BitConverter.ToInt32(shts, 0)); ++count; } else { displaysht.Dequeue(); displaysht.Enqueue(BitConverter.ToInt32(shts, 0)); } } this.waveCanvas.Children.Clear(); pl.Points.Clear(); //short[] shts2 = displaysht.ToArray(); Int32[] shts2 = displaysht.ToArray(); for (Int32 x = 0; x < shts2.Length;++x ) { pl.Points.Add(Normalize(x, shts2[x])); }
this.waveCanvas.Children.Add(pl);
}
Point Normalize(Int32 x, Int32 y) { Point p = new Point();
p.X = 1.0*x / numtodisplay * plW; //p.Y = plH/2.0 - y / (short.MaxValue*1.0) * (plH/2.0); p.Y = plH / 2.0 - y / (Int32.MaxValue * 1.0) * (plH / 2.0); return p; }
}}
And the XAML is given below.
<Window x:Class="WPFs.waveShow" xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation" "="">http://schemas.microsoft.com/winfx/2006/xaml/presentation"<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation" "=""> xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml" "="">http://schemas.microsoft.com/winfx/2006/xaml"<a href="http://schemas.microsoft.com/winfx/2006/xaml" "=""> Title="waveShow" Height="300" Width="300"> <Grid> <Canvas x:Name="waveCanvas" HorizontalAlignment="Left" Height="242" Margin="10,10,0,0" VerticalAlignment="Top" Width="264" Background="Black" />
</Grid></Window>
- How to Draw Waveform While Recording Using NAudio and WPF
- recording and play using Waveform audio interface
- Managing Waveform-Audio Recording
- How to verify the Playback/Recording on WCD9330 Codec using tinymix commands
- How To Merge Two FlowDocument Objects Using C# Code[WPF]
- How to Create WPF Docking Windows Using Code Only
- HOW TO DRAW UML DIAGRAMS
- Recording with a Waveform-Audio Device
- Rendering ink and image to a bitmap using WPF
- Recording MP3 Using Only HTML5 and JavaScript (Recordmp3.js)
- How to draw image to screen directly
- [WPF] How to dynamic bind Menuitem's Header and Icon
- How to Navigate, Group, Sort and Filter Data in WPF
- How to automate Excel using MFC and worksheet functions
- How to make dll and lib using bakefile.
- How to cast enums which are using QFlags and QList?
- How to develop and package Android Wear App using Eclips
- how to start and stop android service by using adb
- Camera driver&V4L2驱动架构介绍
- 详解Java GC的工作原理+Minor GC、FullGC
- 如何让ie兼容max-height,和min-height属性
- 【最大流】POJ-1459 Power Network
- 设计模式六大原则(3):依赖倒置原则
- How to Draw Waveform While Recording Using NAudio and WPF
- 使用数组创建队列
- 爱情,是什么!一个北大老师写的(将自己的心沉淀下来,用心看)
- PHP:exec与system区别
- Unidirectional TSP+uva+简单dp
- maven与checkstyle集成
- aspnet viewstate
- 【黑马程序员】黑马入学准备篇:Java高新技术之 JavaBean和BeanUtils
- c# 扩展方法