在播放 mp3 或 wav 时结合 NAudio Pan 和 JSNet SuperPitch
Posted
技术标签:
【中文标题】在播放 mp3 或 wav 时结合 NAudio Pan 和 JSNet SuperPitch【英文标题】:Combine NAudio Pan and JSNet SuperPitch while playing an mp3 or wav 【发布时间】:2012-12-04 23:40:44 【问题描述】:我一直在使用 C#/WPF 桌面应用程序中出色的 NAudio 库开发专门的 mp3/wav/(wma 后?)播放器。我还添加了 Skype Voice Changer 中使用的 JSNet 库。
我的目标是能够在播放声音文件时修改其 Pan 和 Pitch。
我在播放过程中成功修改了 Pan 属性。我还想出了如何使用 JSNet SuperPitch 对象来修改音高。但是,我只设法在播放前修改了音高。播放开始后,SuperPitch 对象上的控件似乎对声音没有影响。
有没有人成功地将使用 SuperPitch 的音高控制和声像控制与 WaveChannel32.Pan 结合起来?
问题源于效果应用于WaveStream
,然后将其转换为WaveChannel32
以公开.Pan
属性。但是,一旦转换为 WaveChannel32,EffectChain
似乎就不再连接了。
这是一个精简的 WPF 项目,说明了我的基本方法:
简单的 WPF 窗口:
<Window x:Class="NAudioDebug.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Slider x:Name="sldPan" TickFrequency="0.2" Minimum="-1.0" Maximum="1.0" ToolTip="Balance"
Grid.Row="0" TickPlacement="Both" IsSnapToTickEnabled="True"
Value="Binding Path=Pan, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged" />
<Slider x:Name="sldPitch" TickFrequency="1" Minimum="-12" Maximum="12.0" ToolTip="Pitch"
Grid.Row="1" TickPlacement="Both" IsSnapToTickEnabled="True"
Value="Binding Path=Pitch, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged" />
<Button x:Name="btnPlayStop" Content="Play/Stop" Grid.Row="2" Click="btnPlayStop_Click" />
</Grid>
</Window>
这是 XAML 的代码隐藏,上面...
using System.Windows;
namespace NAudioDebug
public partial class MainWindow : Window
private Player _Player;
public MainWindow()
InitializeComponent();
_Player = new Player();
this.DataContext = _Player;
private void btnPlayStop_Click(object sender, RoutedEventArgs e)
_Player.PlayStop();
声音文件播放对象
代码的重要部分是这个对象,它控制播放并提供 WPF 控件可以绑定到的属性。我认为PlayStop()
方法是问题所在。
using System;
using System.ComponentModel;
using JSNet;
using NAudio.Wave;
namespace NAudioDebug
public class Player : INotifyPropertyChanged, IDisposable
private IWavePlayer _WaveOutDevice;
private WaveChannel32 _MainOutputStream;
private EffectStream _EffectStream;
private EffectChain _Effects;
private SuperPitch _PitchEffect;
private bool _bDisposed = false;
public Player()
Pan = 0.0f;
Pitch = 0.0f;
private float _Pan;
public float Pan
get return _Pan;
set
_Pan = value;
if (_MainOutputStream != null)
_MainOutputStream.Pan = _Pan;
OnPropertyChanged("Pan");
private float _Pitch;
public float Pitch
get return _Pitch;
set
_Pitch = value;
if (_PitchEffect != null)
_PitchEffect.Sliders[1].Value = value; // Slider 1 is the pitch bend in semitones from -12 to +12;
OnPropertyChanged("Pitch");
public void PlayStop()
if (_WaveOutDevice != null && _WaveOutDevice.PlaybackState == PlaybackState.Playing)
_WaveOutDevice.Stop();
DisposeAudioResources();
else
// Starting a new stream...
DisposeAudioResources();
_Effects = new EffectChain();
_PitchEffect = new SuperPitch();
_PitchEffect.Sliders[1].Value = _Pitch; // Slider 1 is the pitch bend in semitones from -12 to +12;
_PitchEffect.Sliders[2].Value = 0.0F; // Slider 2 is the pitch bend in octaves.
_Effects.Add(_PitchEffect);
WaveStream inputStream; // NOTE: Use a WaveStream here because the input might be .mp3 or .wav (and later .wma?)
WaveStream mp3Reader = new Mp3FileReader(@"C:\Temp\Test.mp3");
if (mp3Reader.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
mp3Reader = WaveFormatConversionStream.CreatePcmStream(mp3Reader);
inputStream = mp3Reader;
_EffectStream = new EffectStream(_Effects, inputStream);
_MainOutputStream = new WaveChannel32(_EffectStream);
_MainOutputStream.PadWithZeroes = false;
_MainOutputStream.Pan = Pan;
_WaveOutDevice = new WaveOut();
_WaveOutDevice.Init(_MainOutputStream);
_WaveOutDevice.Play();
this._WaveOutDevice.PlaybackStopped += OnPlaybackStopped;
private void OnPlaybackStopped(object sender, EventArgs e)
// Clean up the audio stream resources...
DisposeAudioResources();
private void DisposeAudioResources()
if (_WaveOutDevice != null) _WaveOutDevice.Stop();
if (_MainOutputStream != null) _MainOutputStream.Close(); _MainOutputStream = null;
if (_PitchEffect != null) _PitchEffect = null;
if (_Effects != null) _Effects = null;
if (_EffectStream != null) _EffectStream = null;
if (_WaveOutDevice != null) _WaveOutDevice.Dispose(); _WaveOutDevice = null;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
protected void OnPropertyChanged(string sPropertyName)
OnPropertyChanged(new PropertyChangedEventArgs(sPropertyName));
public event PropertyChangedEventHandler PropertyChanged;
public void Dispose()
Dispose(true);
GC.SuppressFinalize(this);
protected virtual void Dispose(bool bDisposing)
// Check to see if Dispose has already been called...
if (!_bDisposed)
DisposeAudioResources();
_bDisposed = true;
【问题讨论】:
【参考方案1】:您必须记住在滑块值更改后调用effect.Slider()
。每当滑块发生变化时,许多效果都必须重新计算参数,因此出于性能原因,您必须在更改值后通知它。
我希望使用ISampleProvider
接口重新实现我为 SkypeFx 制作的许多效果,这将使它们在 NAudio 中更容易使用。
【讨论】:
以上是关于在播放 mp3 或 wav 时结合 NAudio Pan 和 JSNet SuperPitch的主要内容,如果未能解决你的问题,请参考以下文章
使用 NAudio 转换为 WAV 后使用 SoundPlayer 播放 MP3