核心需求拆解
| 功能 | 技術要點 | 建議實現 |
| 播放/暫停 | 媒體控制 | MediaPlayer / NAudio / LibVLCSharp |
| 靜音 | 音量控制 | 設定 Volume=0 或 IsMuted=true,保留當前音量值供恢復 |
| 重唱 | 進度控制 | Position = TimeSpan.Zero 或 CurrentTime = 0 |
| 切歌 | 播放列表管理 | 維護 List<Song> + 當前索引,Next()/Prev() 切換來源 |
| 歌詞同步 | 解析 .lrc / .krc | 背景執行緒解析時間軸,UI 執行緒高亮顯示 |
| 評分/音高檢測 | FFT/音頻分析 | NAudio 捕獲麥克風輸入,做頻譜比對 (進階) |
推薦技術棧選型
- LibVLCSharp (首選):跨平台、格式支援最全、硬體解碼、穩定。NuGet:
LibVLCSharp.WinForms + VideoLAN.LibVLC.Windows。
- NAudio:純 .NET、低延遲、適合音頻處理/錄音/波形圖,但視頻支援弱。
- MediaPlayerElement (UWP/WPF):WinForm 需用
WindowsFormsHost 裝載,較重。
- DirectShow / MediaFoundation (底層):複雜、不建議直接用。
核心類別設計 (以 LibVLCSharp 為例)
public class KtvPlayer {
private readonly LibVLC _libVLC;
private readonly MediaPlayer _mediaPlayer;
private readonly List _playlist = new();
private int _currentIndex = -1;
private double _volumeBeforeMute = 1.0;
public event Action PositionChanged;
public event Action SongChanged;
public KtvPlayer() {
Core.Initialize();
_libVLC = new LibVLC();
_mediaPlayer = new MediaPlayer(_libVLC);
_mediaPlayer.TimeChanged += (s, e) => PositionChanged?.Invoke(TimeSpan.FromMilliseconds(e.Time));
_mediaPlayer.EndReached += (s, e) => PlayNext();
}
public void LoadPlaylist(IEnumerable songs) {
_playlist.Clear();
_playlist.AddRange(songs);
_currentIndex = -1;
}
public void Play(int index) {
if (index < 0 || index >= _playlist.Count) return;
_currentIndex = index;
var song = _playlist[index];
var media = new Media(_libVLC, new Uri(song.MediaUrl));
_mediaPlayer.Play(media);
SongChanged?.Invoke(song);
}
public void PlayNext() => Play((_currentIndex + 1) % _playlist.Count);
public void PlayPrev() => Play((_currentIndex - 1 + _playlist.Count) % _playlist.Count);
public void Replay() => _mediaPlayer.Time = 0;
public void ToggleMute() {
if (_mediaPlayer.Mute) {
_mediaPlayer.Mute = false;
_mediaPlayer.Volume = (int)(_volumeBeforeMute * 100);
} else {
_volumeBeforeMute = _mediaPlayer.Volume / 100.0;
_mediaPlayer.Mute = true;
}
}
public void SetVolume(double vol) { // 0.0 ~ 1.0
_mediaPlayer.Volume = (int)(vol * 100);
if (vol > 0) _mediaPlayer.Mute = false;
}
}
UI 綁定範例 (WinForms)
public partial class KtvForm : Form {
private readonly KtvPlayer _player = new KtvPlayer();
public KtvForm() {
InitializeComponent();
_player.PositionChanged += ts => lblPosition.Invoke(() => lblPosition.Text = ts.ToString(@"mm\:ss"));
_player.SongChanged += s => {
lblSongName.Invoke(() => lblSongName.Text = s.Title);
// 載入歌詞...
};
}
private void btnPlay_Click(object sender, EventArgs e) => _player.Play(0);
private void btnNext_Click(object sender, EventArgs e) => _player.PlayNext();
private void btnPrev_Click(object sender, EventArgs e) => _player.PlayPrev();
private void btnReplay_Click(object sender, EventArgs e) => _player.Replay();
private void btnMute_Click(object sender, EventArgs e) => _player.ToggleMute();
private void trackVolume_Scroll(object sender, EventArgs e) => _player.SetVolume(trackVolume.Value / 100.0);
}
進階功能擴充
- 歌詞同步:解析 LRC 為
SortedList<TimeSpan, string>,Timer 每 200ms 比對當前時間高亮行。
- 麥克風混音:
NAudio.WaveInEvent 捕獲麥克風 → WaveMixerStream32 與伴奏混音 → 輸出聲卡。
- 評分系統:錄製演唱片段 → FFT 分析基頻 → 與標準旋律比對 (需數位信號處理知識)。
- 網路點歌:SignalR / WebSocket 連接後台,實時接收點歌指令插入播放列表。
避坑指南
- LibVLC 需部署
libvlc.dll 等原生庫,**輸出目錄勾選「複製較新」**或用 NuGet 套件自動處理。
- UI 執行緒更新必須
Control.Invoke 或 async/await 配合 Task.Run。
- 切歌時釋放舊
Media 物件:media.Dispose() 避免記憶體洩漏。
- 全螢幕模式:
_mediaPlayer.SetFullscreen(true) 或 WinForm FormBorderStyle=None, WindowState=Maximized。