首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 應用程式發聲功能實戰:使用 NAudio 即時合成波形音訊

     編輯:關於.NET

前言:為什麼在 WPF 中合成聲音?

現代 UI 不僅是視覺,聽覺回饋能大幅提升使用者體驗 (拖曳音效、操作確認音、無障礙提示)。WPF 雖無內建合成器,但透過 NAudio (CodePlex/GitHub) 可輕鬆實現**運行時波形合成**,無需外部音檔。

環境建置

  1. 安裝 NuGet:Install-Package NAudio (或下載 1.3.8 版 DLL)
  2. 專案屬性 → Build → **Platform target: x86** (NAudio 透過 P/Invoke 呼叫 Win32 waveOut API,需 32 位元)
  3. 引用:using NAudio.Wave;

核心架構:IWaveProvider + WaveOut

  • WaveOut:封裝 Win32 waveOut* API,管理輸出裝置、緩衝區、回調。
  • IWaveProvider:你實作的介面,提供 WaveFormat (採樣率、聲道) 與 Read(byte[] buffer, int offset, int count) 填入波形資料。
  • WaveProvider16:抽象基類,簡化 16-bit PCM 處理,直接用 short[] 而非 byte[]。

範例一:正弦波振盪器

class SineWaveOscillator : WaveProvider16
{
    private double _phaseAngle;
    public double Frequency { get; set; } = 440; // A4
    public short Amplitude { get; set; } = 8192; // 音量

    public SineWaveOscillator(int sampleRate = 44100) : base(sampleRate, 1) {} // Mono

    public override int Read(short[] buffer, int offset, int sampleCount)
    {
        for (int i = 0; i < sampleCount; i++)
        {
            buffer[offset + i] = (short)(Amplitude * Math.Sin(_phaseAngle));
            _phaseAngle += 2 * Math.PI * Frequency / WaveFormat.SampleRate;
            if (_phaseAngle > 2 * Math.PI) _phaseAngle -= 2 * Math.PI;
        }
        return sampleCount;
    }
}

// 播放啟動
var waveOut = new WaveOut();
var osc = new SineWaveOscillator(44100);
waveOut.Init(osc);
waveOut.Play(); // 非阻塞,背景執行緒持續呼叫 Read()

// 停止
osc.Amplitude = 0; // 靜音
waveOut.Stop();
waveOut.Dispose();

挑戰:即時頻率變化的「滑音」問題

WaveOut 預設緩衝區 300ms (3 個 100ms 緩衝區)。Read() 在 **UI 執行緒** 執行,填滿整個緩衝區期間頻率無法變更 → 聽起來像「半音階跳躍」而非平滑滑音。

解決方案:PortamentoSineWaveOscillator (對數音高 + 背景執行緒)

1. 對數音高模型 (MIDI 標準)

// MIDI Note 69 = A4 = 440Hz
// 每半音頻率比 = 2^(1/12)
public double Pitch
{
    get => 69 + 12 * Math.Log(Frequency / 440, 2);
    set => Frequency = 440 * Math.Pow(2, (value - 69) / 12);
}

優點:人耳感知為線性,拖曳距離映射音高極自然。

2. 緩衝區內部插值實現滑音

class PortamentoSineWaveOscillator : WaveProvider16
{
    private double _phaseAngle;
    private double _currentPitch, _targetPitch;
    private int _samplesRemainingInGlide;

    public double Pitch { get => _targetPitch; set => _targetPitch = value; }

    public override int Read(short[] buffer, int offset, int sampleCount)
    {
        // 若音高改變,計算本緩衝區內的插值步長
        if (_currentPitch != _targetPitch)
        {
            _samplesRemainingInGlide = sampleCount; // 簡化:單緩衝區完成滑音
        }

        for (int i = 0; i < sampleCount; i++)
        {
            // 線性插值音高
            if (_samplesRemainingInGlide > 0)
            {
                double t = 1.0 - (double)_samplesRemainingInGlide / sampleCount;
                double pitch = _currentPitch + (_targetPitch - _currentPitch) * t;
                double freq = 440 * Math.Pow(2, (pitch - 69) / 12);
                _phaseAngle += 2 * Math.PI * freq / WaveFormat.SampleRate;
                _samplesRemainingInGlide--;
            }
            else
            {
                _phaseAngle += 2 * Math.PI * Frequency / WaveFormat.SampleRate;
            }

            if (_phaseAngle > 2 * Math.PI) _phaseAngle -= 2 * Math.PI;
            buffer[offset + i] = (short)(Amplitude * Math.Sin(_phaseAngle));
        }
        _currentPitch = _targetPitch;
        return sampleCount;
    }
}

3. 強制 WaveOut 使用回調執行緒

var waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()); // 背景執行緒回調
// 或
var waveOut = new WaveOut(WaveCallbackInfo.NewWindow()); // 隱藏視窗訊息迴圈

此時 Read() 在背景執行緒執行,UI 執行緒可隨時修改 Pitch,滑音極順暢。

完整互動範例:AudibleDragging

  • 視窗中心放 7 個彩色方塊
  • 滑鼠拖曳方塊 → 計算到中心距離 → 映射 Pitch = 60 + 12 * distance / 200 (每 200 像素升一個八度)
  • 拖曳過程中即時播放滑音,釋放停止

原始碼下載:文章附件或 NAudio 官方範例

進階:混音器與 MIDI 整合

  • 繼承 WaveProvider16 實作 Mixer:多個 IWaveProvider 輸入 → 加權加總 → 輸出合成波形。
  • NAudio 內建 MidiOut、MidiEvent 類別,可發送 MIDI 訊息給系統合成器/外部裝置,播放樂器音色。

效能與限制提醒

項目建議
採樣率44.1kHz (CD 品質) 或 48kHz (專業音訊)
緩衝區大小延遲 < 50ms 覺得即時;DesiredLatency 設 100-200ms 平衡穩定度
平台僅限 Windows (Win32 waveOut);**不可用於 Silverlight / UWP / .NET Core 跨平台**
授權NAudio: MIT License,商業可用

結語

WPF 配合 NAudio 即時合成波形,打破「應用程式應保持無聲」的傳統框架。從簡單的正弦波到複雜的加成合成、MIDI 控制,聲音成為 UI 互動的新維度。掌握 IWaveProvider、緩衝區管理與執行緒模型,即可為應用賦予豐富的聽覺體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved