在播放mp3或wav时结合NAudio Pan和JSNet SuperPitch

时间:2012-12-04 23:40:45

标签: wpf naudio

我一直在使用C#/ WPF桌面应用程序中优秀的NAudio库来处理专门的mp3 / wav /(wma later?)播放器。我还添加了Skype Voice Changer中使用的JSNet库。

我的目标是能够在播放时修改声音文件的平移音高

我在播放期间成功修改了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 个答案:

答案 0 :(得分:1)

您必须记住在滑块值更改后调用effect.Slider()。许多效果必须在滑块更改时重新计算参数,因此性能原因,您必须在更改值后通知它。

我希望通过ISampleProvider界面重新实现我为SkypeFx所做的很多效果,这样可以让它们更容易在NAudio中使用。