目的:
利用NAudio中提供的WaveProvider编写出自定义的声音播放器,能够播放自己想要的声音数据。
一、自定义的WaveProvider
1 | class SelfPlayer: WaveProvider16 //继承自WaveProvider16 |
在主函数中使用如下方式播放测试音频:1
2
3
4
5
6
7
8
9
10
11
12public partial class MainWindow : Window
{
SelfPlayer player=null;
public MainWindow()
{
InitializeComponent();
player = new SelfPlayer();
player.selfWaveout = new NAudio.Wave.WaveOut();
player.selfWaveout.Init(player);
player.selfWaveout.Play();
}
}
二、独立线程的播放
通常声音数据的获取和播放会使用独立的线程,这是如果采用下面的方式,则不能获得连续的声音:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public partial class MainWindow : Window
{
SelfPlayer player=null;
public MainWindow()
{
InitializeComponent();
player = new SelfPlayer();
Thread th = new Thread(threadPlay); //在新线程中播放声音
th.Start();
}
private void threadPlay()
{
player.selfWaveout = new NAudio.Wave.WaveOut();
player.selfWaveout.Init(player);
player.selfWaveout.Play();
}
}
只听到首次较短时间的声音,之后就没有声音播出了。暂时还不清楚原因,可能的原因之一是不能用Player自身的Waveout来播放自身,于是编写如下测试:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public partial class MainWindow : Window
{
SelfPlayer player=null;
WaveOut waveout = null;
public MainWindow()
{
InitializeComponent();
player = new SelfPlayer();
Thread th = new Thread(threadPlay);
th.Start();
}
private void threadPlay()
{
waveout = new NAudio.Wave.WaveOut();
waveout.Init(player);
waveout.Play();
}
}
结果发现依然只能播出短暂的声音。可能的原因:Waveout在Init之后需要一定的延迟,但经测试仍然不对。最后将初始化的语句放到线程之外,就可以了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public partial class MainWindow : Window
{
SelfPlayer player=null;
WaveOut waveout = null;
public MainWindow()
{
InitializeComponent();
player = new SelfPlayer();
waveout = new NAudio.Wave.WaveOut();
waveout.Init(player);
Thread th = new Thread(threadPlay);
th.Start();
}
private void threadPlay()
{
waveout.Play();
}
}
类似地,下面的代码也是可行的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72public partial class MainWindow : Window
{
SelfPlayer player=null;
public MainWindow()
{
InitializeComponent();
player = new SelfPlayer();
player.selfWaveout = new NAudio.Wave.WaveOut();
player.selfWaveout.Init(player);
Thread th = new Thread(threadPlay);
th.Start();
}
private void threadPlay()
{
player.selfWaveout.Play();
}
}
```
事实上,可以把许多播放的细节都隐藏到自定义的Player中:
``` csharp
class SelfPlayer: WaveProvider16
{
public SelfPlayer(): base() { }
public SelfPlayer(int sampleRate): base(sampleRate, 1) { }
public int audioRate = 1000;
double Amplitude = 0.5;
public short[] waveData = new short[2000];
public int playPos = 0;
double t = 0;
public WaveOut selfWaveout = null;
public void Init()//自初始化
{
this.selfWaveout = new WaveOut();
this.selfWaveout.Init(this);
}
public void ThPlay()//线程自播放
{
Thread thplay = new Thread(ThPlaying);
thplay.Start();
}
private void ThPlaying()
{
this.Play();
}
public void Play()//自播放
{
this.selfWaveout.Play();
}
public override int Read(short[] buffer, int offset, int sampleCount)
{
short dataTemp = 0;
for (int index = 0; index < sampleCount; index++)
{
t += 1/(double)base.WaveFormat.SampleRate;
dataTemp = (short)(short.MaxValue * Amplitude * Math.Sin(2 * Math.PI * audioRate * t));
buffer[offset + index] = dataTemp;
waveData[playPos] = dataTemp;
playPos++;
if (playPos == 2000)
playPos = 0;
}
return sampleCount;
}
}
这样在调用函数中就可以比较简洁:1
2
3
4
5
6
7
8
9
10
11public partial class MainWindow : Window
{
SelfPlayer player=null;
public MainWindow()
{
InitializeComponent();
player = new SelfPlayer();
player.Init();
player.ThPlay();
}
}
总结
使用NAudio时应注意,Waveout的初始化(Init
)和播放(Play
)不能在同一个进程中。