如何在C# NAudio音乐播放器中创建搜索栏?
我是NAudio和C#的新手,我设法创建了一个简单的MP3播放器,你可以选择一个文件并播放它。 它还有一个播放/暂停按钮。
我现在想添加一个搜索栏,但不知道如何做到这一点。 波形样式的搜索条也可以吗?
openButton单击处理程序
private void openButton_Click(object sender, EventArgs e) { OpenFileDialog open = new OpenFileDialog(); open.Filter = "Audio File|*.mp3;"; if (open.ShowDialog() != DialogResult.OK) return; CloseWaveOut(); // disposes the waveOutDevice and audiofilereader waveOutDevice = new WaveOut(); audioFileReader = new AudioFileReader(open.FileName); waveOutDevice.Init(audioFileReader); waveOutDevice.Play(); pauseButton.Enabled = true; }
除了纯粹基于UI的问题之外,您还需要做三件基本事情:
-
读歌长度。
-
获取播放位置。
-
设置播放位置。
歌曲长度和当前播放位置非常简单 – 它们可通过WaveStream
对象的TotalTime
和CurrentTime
属性获得,这意味着您的audioFileReader
对象也支持它们。 构造完成后, audioFileReader.TotalTime
将为您提供一个TimeSpan
对象,其中包含文件的总长度, audioFileReader.CurrentTime
将为您提供当前播放位置。
您还可以通过分配audioFileReader.CurrentTime
来设置播放位置…但这样做是一个棘手的过程,除非您知道自己在做什么。 以下代码有时会跳过2.5秒,但有时可能会崩溃:
audioFileReader.CurrentTime = audioFileReader.CurrentTime.Add(TimeSpan.FromSeconds(2.5));
这里的问题是由于几个原因(包括在后台完成的浮点数学运算),结果流位置(以字节为单位)可能无法正确对齐到样本的开始。 这可以快速将您的输出变为垃圾。
当您想要更改播放位置时,更好的选择是使用流的Position
属性。 Position
是当前播放位置(以字节为单位),因此更难以处理。 虽然不是太多:
audioFileReader.Position += audioFileReader.WaveFormat.AverageBytesPerSecond;
如果你向前或向后踩一整秒,那很好。 如果没有,您需要确保始终定位在样本边界,使用WaveFormat.BlockAlign
属性来确定这些边界的位置。
// Calculate new position long newPos = audioFileReader.Position + (long)(audioFileReader.WaveFormat.AverageBytesPerSecond * 2.5); // Force it to align to a block boundary if ((newPos % audioFileReader.WaveFormat.BlockAlign) != 0) newPos -= newPos % audioFileReader.WaveFormat.BlockAlign; // Force new position into valid range newPos = Math.Max(0, Math.Min(audioFileReader.Length, newPos)); // set position audioFileReader.Position = newPos;
这里要做的简单事情是定义一组WaveStream
类的扩展,它将在搜索操作期间处理块对齐。 基本的对齐块操作可以通过变量来调用,这些变量只是根据您输入的内容计算新位置,所以这样的事情:
public static class WaveStreamExtensions { // Set position of WaveStream to nearest block to supplied position public static void SetPosition(this WaveStream strm, long position) { // distance from block boundary (may be 0) long adj = position % strm.WaveFormat.BlockAlign; // adjust position to boundary and clamp to valid range long newPos = Math.Max(0, Math.Min(strm.Length, position - adj)); // set playback position strm.Position = newPos; } // Set playback position of WaveStream by seconds public static void SetPosition(this WaveStream strm, double seconds) { strm.SetPosition((long)(seconds * strm.WaveFormat.AverageBytesPerSecond)); } // Set playback position of WaveStream by time (as a TimeSpan) public static void SetPosition(this WaveStream strm, TimeSpan time) { strm.SetPosition(time.TotalSeconds); } // Set playback position of WaveStream relative to current position public static void Seek(this WaveStream strm, double offset) { strm.SetPosition(strm.Position + (long)(offset* strm.WaveFormat.AverageBytesPerSecond)); } }
有了这个,你可以调用audioFileReader.SetPosition(10.0)
跳转到播放位置00:00:10.0
,调用audioFileReader.Seek(-5)
跳回5秒等,而不用担心寻找到一半的方式通过样本。
所以…在表单中添加一些按钮并将它们设置为调用Seek
方法,使用+/-值来移动。 然后添加一些可用于显示和设置播放位置的滑块。 扔进一个计时器,将滑块位置更新到当前播放位置即可完成。
有一个很好的答案,但我想在WPF中添加另一种构建搜索栏的方法,因为我也在开发一个类似的项目。
这是寻求者的XAML代码:
ViewModel中的CurrentTrackLenght
和CurrentTrackPosition
是:
public double CurrentTrackLenght { get { return _currentTrackLenght; } set { if (value.Equals(_currentTrackLenght)) return; _currentTrackLenght = value; OnPropertyChanged(nameof(CurrentTrackLenght)); } } public double CurrentTrackPosition { get { return _currentTrackPosition; } set { if (value.Equals(_currentTrackPosition)) return; _currentTrackPosition = value; OnPropertyChanged(nameof(CurrentTrackPosition)); } }
这个想法非常简单; 一旦我们开始玩:
首先,我们在几秒钟内得到音频文件的长度并将其分配给CurrentTrackLenght
属性,它将被绑定到seekbar的Maximum
属性。 然后,当我们播放音频文件时,我们不断更新CurrentTrackPosition
属性,该属性又驱动搜索栏的Value
属性。
因此,当我们按下“播放”按钮时,我们的ViewModel中的以下命令将运行:
private void StartPlayback(object p) { if (_playbackState == PlaybackState.Stopped) { if (CurrentTrack != null) { _audioPlayer.LoadFile(CurrentTrack.Filepath, CurrentVolume); CurrentTrackLenght = _audioPlayer.GetLenghtInSeconds(); } } _audioPlayer.TogglePlayPause(CurrentVolume); }
_audioPlayer
是我用来简化播放/暂停/停止的抽象,因此您可以用自己的代码替换它们。 但重要的是:
CurrentTrackLenght = _audioPlayer.GetLenghtInSeconds();
AudioPlayer
GetLenghtInSeconds()
的代码是:
public double GetLenghtInSeconds() { if (_audioFileReader != null) { return _audioFileReader.TotalTime.TotalSeconds; } else { return 0; } }
因此,我们初始化我们开始播放的每个音频文件的搜索栏的Maximum
。
现在,我们需要在播放音频时更新我们的搜索栏。
首先,我们需要在几秒钟内确定音频文件的当前位置。 我在这里选择秒数,因为我们的搜索栏的Maximum
也是以秒为单位,因此它们将正确匹配。
为此,我们需要在AudioPlayer
使用以下方法:
public double GetPositionInSeconds() { if (_audioFileReader != null) { return _audioFileReader.CurrentTime.TotalSeconds; } else { return 0; } }
完成此代码后,我们可以继续使用ViewModel。 首先,我们需要在构造函数中设置一个计时器。
var timer = new System.Timers.Timer(); timer.Interval = 300; timer.Elapsed += Timer_Elapsed; timer.Start();
并添加Timer_Elapsed()
和UpdateSeekBar()
方法:
private void UpdateSeekBar() { if (_playbackState == PlaybackState.Playing) { CurrentTrackPosition = _audioPlayer.GetPositionInSeconds(); } } private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { UpdateSeekBar(); }
完成此操作后,现在当我们播放音频文件时,我们的搜索栏应该按预期移动。
现在对于实际的搜索部分,首先我们需要在AudioPlayer
类中使用SetPosition()
方法。
public void SetPosition(double value) { if (_audioFileReader != null) { _audioFileReader.CurrentTime = TimeSpan.FromSeconds(value); } }
此代码将当前时间设置为我们传递的值,从而有效地寻找新位置。
最后,我们需要4个方法来为PreviewMouseDown
和PreviewMouseUp
事件完成ViewModel命令。
private void TrackControlMouseDown(object p) { _audioPlayer.Pause(); } private void TrackControlMouseUp(object p) { _audioPlayer.SetPosition(CurrentTrackPosition); _audioPlayer.Play(NAudio.Wave.PlaybackState.Paused, CurrentVolume); } private bool CanTrackControlMouseDown(object p) { if (_playbackState == PlaybackState.Playing) { return true; } return false; } private bool CanTrackControlMouseUp(object p) { if (_playbackState == PlaybackState.Paused) { return true; } return false; }
如果您想确切了解这些是如何实现的,您可以转到我的github页面并查看完整的实现。
上述就是C#学习教程:如何在C# NAudio音乐播放器中创建搜索栏?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/992149.html