在播放 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 库。

我的目标是能够在播放声音文件时修改其 PanPitch

我在播放过程中成功修改了 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

如何使用 C# NAudio 操作字节?

在 NAudio 中附加 WAV 标头

使用 NAudio 从内存流中播放 .wma

使用 NAudio/Lame 将 .wav 转换为 mp3 时如何设置通道和采样率?

使用 NAudio 处理后播放 wav 文件