我一直在使用C#/ WPF桌面应用程序中优秀的NAudio库来处理专门的mp3 / wav /(wma later?)播放器。我还添加了Skype Voice Changer中使用的JSNet库。
我的目标是能够在播放时修改声音文件的平移和音高。
我在播放期间成功修改了Pan属性。我还想出了如何使用JSNet SuperPitch对象来修改音高。但是,我只是设法在播放之前修改音高 。一旦开始播放,SuperPitch对象上的控件似乎对声音没有影响。
有没有人成功将使用SuperPitch和平移控制的音高控制与WaveChannel32.Pan相结合?
问题源于这样一个事实,即效果应用于WaveStream
,然后转换为WaveChannel32
以展示.Pan
属性。但是,一旦转换为WaveChannel32,EffectChain
似乎不再连接。
这是一个精简的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;
}
}
}
}
答案 0 :(得分:1)
您必须记住在滑块值更改后调用effect.Slider()
。许多效果必须在滑块更改时重新计算参数,因此性能原因,您必须在更改值后通知它。
我希望通过ISampleProvider
界面重新实现我为SkypeFx所做的很多效果,这样可以让它们更容易在NAudio中使用。