Bagikan melalui


Grafik audio

Artikel ini memperlihatkan cara menggunakan API di namespace Windows.Media.Audio untuk membuat grafik audio untuk skenario perutean, pencampuran, dan pemrosesan audio.

Grafik audio adalah sekumpulan simpul audio yang saling terhubung di mana data audio mengalir.

  • Simpul input audio menyediakan data audio ke grafik dari perangkat input audio, file audio, atau dari kode kustom. lat

  • Simpul output audio adalah tujuan untuk audio yang diproses oleh grafik. Audio dapat dirutekan dari grafik ke perangkat output audio, file audio, atau kode kustom.

  • Simpul submix mengambil audio dari satu atau beberapa simpul dan menggabungkannya ke dalam satu output yang dapat dirutekan ke simpul lain dalam grafik.

Setelah semua simpul dibuat dan koneksi di antaranya disiapkan, Anda cukup memulai grafik audio dan data audio mengalir dari simpul input, melalui simpul submiks apa pun, ke simpul output. Model ini membuat skenario seperti merekam dari mikrofon perangkat ke file audio, memutar audio dari file ke speaker perangkat, atau mencampur audio dari beberapa sumber dengan cepat dan mudah diimplementasikan.

Skenario tambahan diaktifkan dengan penambahan efek audio ke grafik audio. Setiap simpul dalam grafik audio dapat diisi dengan nol atau lebih efek audio yang melakukan pemrosesan audio pada audio yang melewati simpul. Ada beberapa efek bawaan seperti echo, equalizer, limiting, dan reverb yang dapat dilampirkan ke simpul audio hanya dengan beberapa baris kode. Anda juga dapat membuat efek audio kustom Anda sendiri yang berfungsi persis sama dengan efek bawaan.

Catatan

Sampel UWP AudioGraph mengimplementasikan kode yang dibahas dalam gambaran umum ini. Anda dapat mengunduh sampel untuk melihat kode dalam konteks atau untuk digunakan sebagai titik awal untuk aplikasi Anda sendiri.

Memilih Windows Runtime AudioGraph atau XAudio2

API grafik audio Windows Runtime menawarkan fungsionalitas yang juga dapat diimplementasikan dengan menggunakan API XAudio2 berbasis COM. Berikut ini adalah fitur kerangka kerja grafik audio Windows Runtime yang berbeda dari XAudio2.

API grafik audio Windows Runtime:

  • Secara signifikan lebih mudah digunakan daripada XAudio2.
  • Dapat digunakan dari C# selain didukung untuk C++.
  • Dapat menggunakan file audio, termasuk format file terkompresi, secara langsung. XAudio2 hanya beroperasi pada buffer audio dan tidak menyediakan kemampuan I/O file apa pun.
  • Dapat menggunakan alur audio latensi rendah di Windows 10.
  • Mendukung peralihan titik akhir otomatis saat parameter titik akhir default digunakan. Misalnya, jika pengguna beralih dari speaker perangkat ke headset, audio secara otomatis dialihkan ke input baru.

Kelas AudioGraph

Kelas AudioGraph adalah induk dari semua simpul yang membentuk grafik. Gunakan objek ini untuk membuat instans dari semua jenis simpul audio. Buat instans kelas AudioGraph dengan menginisialisasi objek AudioGraphSettings yang berisi pengaturan konfigurasi untuk grafik, lalu panggil AudioGraph.CreateAsync. CreateAudioGraphResult yang dikembalikan memberikan akses ke grafik audio yang dibuat atau memberikan nilai kesalahan jika pembuatan grafik audio gagal.

AudioGraph audioGraph;
private async Task InitAudioGraph()
{

    AudioGraphSettings settings = new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Media);

    CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
    if (result.Status != AudioGraphCreationStatus.Success)
    {
        ShowErrorMessage("AudioGraph creation error: " + result.Status.ToString());
    }

    audioGraph = result.Graph;

}
  • Semua jenis node audio dibuat dengan menggunakan metode Create* dari kelas AudioGraph .

  • Metode AudioGraph.Start menyebabkan grafik audio mulai memproses data audio. Metode AudioGraph.Stop menghentikan pemrosesan audio. Setiap simpul dalam grafik dapat dimulai dan dihentikan secara independen saat grafik berjalan, tetapi tidak ada simpul yang aktif saat grafik dihentikan. ResetAllNodes menyebabkan semua simpul dalam grafik membuang data apa pun yang saat ini ada di buffer audio mereka.

  • Peristiwa QuantumStarted terjadi ketika grafik memulai pemrosesan kuantum data audio baru. Peristiwa QuantumProcessed terjadi ketika pemrosesan kuantum selesai.

  • Satu-satunya properti AudioGraphSettings yang diperlukan adalah AudioRenderCategory. Menentukan nilai ini memungkinkan sistem mengoptimalkan alur audio untuk kategori yang ditentukan.

  • Ukuran kuantum grafik audio menentukan jumlah sampel yang diproses pada satu waktu. Secara default, ukuran kuantum adalah 10 mdtk berdasarkan laju sampel default. Jika Anda menentukan ukuran kuantum kustom dengan mengatur properti DesiredSamplesPerQuantum, Anda juga harus mengatur properti QuantumSizeSelectionMode ke ClosestToDesired atau nilai yang disediakan diabaikan. Jika nilai ini digunakan, sistem akan memilih ukuran kuantum sedekat mungkin dengan yang Anda tentukan. Untuk menentukan ukuran kuantum aktual, periksa SamplesPerQuantum dari AudioGraph setelah dibuat.

  • Jika Anda hanya berencana untuk menggunakan grafik audio dengan file dan tidak berencana untuk menghasilkan ke perangkat audio, disarankan agar Anda menggunakan ukuran kuantum default dengan tidak mengatur properti DesiredSamplesPerQuantum.

  • Properti DesiredRenderDeviceAudioProcessing menentukan jumlah pemrosesan yang dilakukan perangkat render utama pada output grafik audio. Pengaturan Default memungkinkan sistem menggunakan pemrosesan audio default untuk kategori render audio yang ditentukan. Pemrosesan ini dapat secara signifikan meningkatkan suara audio di beberapa perangkat, terutama perangkat seluler dengan speaker kecil. Pengaturan Mentah dapat meningkatkan performa dengan meminimalkan jumlah pemrosesan sinyal yang dilakukan, tetapi dapat mengakibatkan kualitas suara yang lebih rendah pada beberapa perangkat.

  • Jika QuantumSizeSelectionMode diatur ke LowestLatency, grafik audio akan secara otomatis menggunakan Raw untuk DesiredRenderDeviceAudioProcessing.

  • Dimulai dengan Windows 10, versi 1803, Anda dapat mengatur properti AudioGraphSettings.MaxPlaybackSpeedFactor untuk mengatur nilai maksimum yang digunakan untuk properti AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactor, dan MediaSourceInputNode.PlaybackSpeedFactor. Ketika grafik audio mendukung faktor kecepatan pemutaran yang lebih besar dari 1, sistem harus mengalokasikan memori tambahan untuk mempertahankan buffer data audio yang memadai. Untuk alasan ini, mengatur MaxPlaybackSpeedFactor ke nilai terendah yang diperlukan oleh aplikasi Anda akan mengurangi konsumsi memori aplikasi Anda. Jika aplikasi Anda hanya akan memutar konten dengan kecepatan normal, disarankan agar Anda mengatur MaxPlaybackSpeedFactor ke 1.

  • EncodingProperties menentukan format audio yang digunakan oleh grafik. Hanya format float 32-bit yang didukung.

  • PrimaryRenderDevice mengatur perangkat render utama untuk grafik audio. Jika Anda tidak mengatur ini, perangkat sistem default akan digunakan. Perangkat render utama digunakan untuk menghitung ukuran kuantum untuk simpul lain dalam grafik. Jika tidak ada perangkat render audio yang ada di sistem, pembuatan grafik audio akan gagal.

Anda dapat membiarkan grafik audio menggunakan perangkat render audio default atau menggunakan kelas Windows.Devices.Enumeration.DeviceInformation untuk mendapatkan daftar perangkat render audio sistem yang tersedia dengan memanggil FindAllAsync dan meneruskan pemilih perangkat render audio yang dikembalikan oleh Windows.Media.Devices.MediaDevice.GetAudioRenderSelector. Anda dapat memilih salah satu objek DeviceInformation yang dikembalikan secara terprogram atau menampilkan UI untuk memungkinkan pengguna memilih perangkat lalu menggunakannya untuk mengatur properti PrimaryRenderDevice.

Windows.Devices.Enumeration.DeviceInformationCollection devices =
 await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Media.Devices.MediaDevice.GetAudioRenderSelector());

// Show UI to allow the user to select a device
Windows.Devices.Enumeration.DeviceInformation selectedDevice = ShowMyDeviceSelectionUI(devices);


settings.PrimaryRenderDevice = selectedDevice;

Simpul input perangkat

Simpul input perangkat menyalurkan audio ke dalam grafik dari perangkat pengambilan audio yang terhubung ke sistem, seperti mikrofon. Buat objek DeviceInputNode yang menggunakan perangkat penangkapan audio default sistem dengan memanggil CreateDeviceInputNodeAsync. Berikan AudioRenderCategory untuk memungkinkan sistem mengoptimalkan alur audio untuk kategori yang ditentukan.

AudioDeviceInputNode deviceInputNode;
private async Task CreateDeviceInputNode()
{
    // Create a device output node
    CreateAudioDeviceInputNodeResult result = await audioGraph.CreateDeviceInputNodeAsync(Windows.Media.Capture.MediaCategory.Media);

    if (result.Status != AudioDeviceNodeCreationStatus.Success)
    {
        // Cannot create device output node
        ShowErrorMessage(result.Status.ToString());
        return;
    }

    deviceInputNode = result.DeviceInputNode;
}

Jika Anda ingin menentukan perangkat penangkapan audio tertentu untuk simpul input perangkat, Anda dapat menggunakan kelas Windows.Devices.Enumeration.DeviceInformation untuk mendapatkan daftar perangkat pengambilan audio sistem yang tersedia dengan memanggil FindAllAsync dan meneruskan pemilih perangkat render audio yang dikembalikan oleh Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector. Anda dapat memilih salah satu objek DeviceInformation yang dikembalikan secara terprogram atau menampilkan UI untuk memungkinkan pengguna memilih perangkat lalu meneruskannya ke CreateDeviceInputNodeAsync.

Windows.Devices.Enumeration.DeviceInformationCollection devices =
 await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector());

// Show UI to allow the user to select a device
Windows.Devices.Enumeration.DeviceInformation selectedDevice = ShowMyDeviceSelectionUI(devices);

CreateAudioDeviceInputNodeResult result =
    await audioGraph.CreateDeviceInputNodeAsync(Windows.Media.Capture.MediaCategory.Media, audioGraph.EncodingProperties, selectedDevice);

Simpul output perangkat

Simpul output perangkat mendorong audio dari grafik ke perangkat render audio, seperti speaker atau headset. Buat DeviceOutputNode dengan memanggil CreateDeviceOutputNodeAsync. Simpul output menggunakan PrimaryRenderDevice dari grafik audio.

AudioDeviceOutputNode deviceOutputNode;
private async Task CreateDeviceOutputNode()
{
    // Create a device output node
    CreateAudioDeviceOutputNodeResult result = await audioGraph.CreateDeviceOutputNodeAsync();

    if (result.Status != AudioDeviceNodeCreationStatus.Success)
    {
        // Cannot create device output node
        ShowErrorMessage(result.Status.ToString());
        return;
    }

    deviceOutputNode = result.DeviceOutputNode;
}

Simpul input file

Simpul input file memungkinkan Anda untuk memberi umpan data dari file audio ke dalam grafik. Buat AudioFileInputNode dengan memanggil CreateFileInputNodeAsync.

AudioFileInputNode fileInputNode;
private async Task CreateFileInputNode()
{
    if (audioGraph == null)
        return;

    FileOpenPicker filePicker = new FileOpenPicker();
    filePicker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
    filePicker.FileTypeFilter.Add(".mp3");
    filePicker.FileTypeFilter.Add(".wav");
    filePicker.FileTypeFilter.Add(".wma");
    filePicker.FileTypeFilter.Add(".m4a");
    filePicker.ViewMode = PickerViewMode.Thumbnail;
    StorageFile file = await filePicker.PickSingleFileAsync();

    // File can be null if cancel is hit in the file picker
    if (file == null)
    {
        return;
    }
    CreateAudioFileInputNodeResult result = await audioGraph.CreateFileInputNodeAsync(file);

    if (result.Status != AudioFileNodeCreationStatus.Success)
    {
        ShowErrorMessage(result.Status.ToString());
    }

    fileInputNode = result.FileInputNode;
}
  • Simpul input file mendukung format file berikut: mp3, wav, wma, m4a.
  • Atur properti StartTime untuk menentukan offset waktu ke dalam file tempat pemutaran harus dimulai. Jika properti ini null, awal file digunakan. Atur properti EndTime untuk menentukan offset waktu ke dalam file di mana pemutaran harus berakhir. Jika properti ini null, akhir file akan digunakan. Nilai waktu mulai harus lebih rendah dari nilai waktu akhir, dan nilai waktu akhir harus kurang dari atau sama dengan durasi file audio, yang dapat ditentukan dengan memeriksa nilai properti Durasi .
  • Cari ke posisi dalam file audio dengan memanggil Cari dan tentukan offset waktu ke dalam file tempat posisi pemutaran harus dipindahkan. Nilai yang ditentukan harus berada dalam rentang StartTime dan EndTime. Dapatkan posisi pemutaran simpul saat ini dengan properti Posisi baca-saja.
  • Aktifkan perulangan file audio dengan mengatur properti LoopCount. Ketika non-null, nilai ini menunjukkan berapa kali file akan diputar setelah pemutaran awal. Jadi, misalnya, mengatur LoopCount ke 1 akan menyebabkan file diputar total 2 kali, dan mengaturnya ke 5 akan menyebabkan file diputar 6 kali secara total. Mengatur LoopCount ke null menyebabkan file diulang tanpa batas waktu. Untuk menghentikan perulangan, atur nilai ke 0.
  • Sesuaikan kecepatan di mana file audio diputar kembali dengan mengatur PlaybackSpeedFactor. Nilai 1 menunjukkan kecepatan asli file, .5 adalah setengah kecepatan, dan 2 adalah kecepatan ganda.

Simpul input MediaSource

Kelas MediaSource menyediakan cara umum untuk mereferensikan media dari sumber yang berbeda dan mengekspos model umum untuk mengakses data media terlepas dari format media yang mendasar yang dapat menjadi file pada disk, aliran, atau sumber jaringan streaming adaptif. Node **MediaSourceAudioInputNode memungkinkan Anda mengarahkan data audio dari MediaSource ke grafik audio. Buat MediaSourceAudioInputNode dengan memanggil CreateMediaSourceAudioInputNodeAsync, meneruskan objek MediaSource yang mewakili konten yang ingin Anda putar. **CreateMediaSourceAudioInputNodeResult dikembalikan yang dapat Anda gunakan untuk menentukan status operasi dengan memeriksa properti Status. Jika statusnya adalah Berhasil, Anda bisa mendapatkan MediaSourceAudioInputNode yang dibuat dengan mengakses properti Node. Contoh berikut menunjukkan pembuatan simpul dari objek AdaptiveMediaSource yang mewakili streaming konten melalui jaringan. Untuk informasi selengkapnya tentang bekerja dengan MediaSource, lihat Item media, daftar putar, dan trek. Untuk informasi selengkapnya tentang konten media streaming melalui internet, lihat Streaming adaptif.

MediaSourceAudioInputNode mediaSourceInputNode;
private async Task CreateMediaSourceInputNode(System.Uri contentUri)
{
    if (audioGraph == null)
        return;

    var adaptiveMediaSourceResult = await AdaptiveMediaSource.CreateFromUriAsync(contentUri);
    if(adaptiveMediaSourceResult.Status != AdaptiveMediaSourceCreationStatus.Success)
    {
        Debug.WriteLine("Failed to create AdaptiveMediaSource");
        return;
    }

    var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(adaptiveMediaSourceResult.MediaSource);
    CreateMediaSourceAudioInputNodeResult mediaSourceAudioInputNodeResult =
        await audioGraph.CreateMediaSourceAudioInputNodeAsync(mediaSource);

    if (mediaSourceAudioInputNodeResult.Status != MediaSourceAudioInputNodeCreationStatus.Success)
    {
        switch (mediaSourceAudioInputNodeResult.Status)
        {
            case MediaSourceAudioInputNodeCreationStatus.FormatNotSupported:
                Debug.WriteLine("The MediaSource uses an unsupported format");
                break;
            case MediaSourceAudioInputNodeCreationStatus.NetworkError:
                Debug.WriteLine("The MediaSource requires a network connection and a network-related error occurred");
                break;
            case MediaSourceAudioInputNodeCreationStatus.UnknownFailure:
            default:
                Debug.WriteLine("An unknown error occurred while opening the MediaSource");
                break;
        }
        return;
    }

    mediaSourceInputNode = mediaSourceAudioInputNodeResult.Node;
}

Untuk menerima pemberitahuan ketika pemutaran telah mencapai akhir konten MediaSource, daftarkan handler untuk peristiwa MediaSourceCompleted.

mediaSourceInputNode.MediaSourceCompleted += MediaSourceInputNode_MediaSourceCompleted;
private void MediaSourceInputNode_MediaSourceCompleted(MediaSourceAudioInputNode sender, object args)
{
    audioGraph.Stop();
}

Saat memutar file dari disk mungkin selalu berhasil diselesaikan, media yang dialirkan dari sumber jaringan mungkin gagal selama pemutaran karena perubahan koneksi jaringan atau masalah lain yang berada di luar kontrol grafik audio. Jika MediaSource menjadi tidak dapat diputar selama pemutaran, grafik audio akan menaikkan peristiwa UnrecoverableErrorOccurred. Anda dapat menggunakan handler untuk kejadian ini untuk menghentikan dan membuang grafik audio lalu menginisialisasi ulang grafik Anda.

audioGraph.UnrecoverableErrorOccurred += AudioGraph_UnrecoverableErrorOccurred;
private void AudioGraph_UnrecoverableErrorOccurred(AudioGraph sender, AudioGraphUnrecoverableErrorOccurredEventArgs args)
{
    if (sender == audioGraph && args.Error != AudioGraphUnrecoverableError.None)
    {
        Debug.WriteLine("The audio graph encountered and unrecoverable error.");
        audioGraph.Stop();
        audioGraph.Dispose();
        InitAudioGraph();
    }
}

Simpul output file

Simpul output file memungkinkan Anda mengarahkan data audio dari grafik ke dalam file audio. Buat AudioFileOutputNode dengan memanggil CreateFileOutputNodeAsync.

AudioFileOutputNode fileOutputNode;
private async Task CreateFileOutputNode()
{
    FileSavePicker saveFilePicker = new FileSavePicker();
    saveFilePicker.FileTypeChoices.Add("Pulse Code Modulation", new List<string>() { ".wav" });
    saveFilePicker.FileTypeChoices.Add("Windows Media Audio", new List<string>() { ".wma" });
    saveFilePicker.FileTypeChoices.Add("MPEG Audio Layer-3", new List<string>() { ".mp3" });
    saveFilePicker.SuggestedFileName = "New Audio Track";
    StorageFile file = await saveFilePicker.PickSaveFileAsync();

    // File can be null if cancel is hit in the file picker
    if (file == null)
    {
        return;
    }

    Windows.Media.MediaProperties.MediaEncodingProfile mediaEncodingProfile;
    switch (file.FileType.ToString().ToLowerInvariant())
    {
        case ".wma":
            mediaEncodingProfile = MediaEncodingProfile.CreateWma(AudioEncodingQuality.High);
            break;
        case ".mp3":
            mediaEncodingProfile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High);
            break;
        case ".wav":
            mediaEncodingProfile = MediaEncodingProfile.CreateWav(AudioEncodingQuality.High);
            break;
        default:
            throw new ArgumentException();
    }


    // Operate node at the graph format, but save file at the specified format
    CreateAudioFileOutputNodeResult result = await audioGraph.CreateFileOutputNodeAsync(file, mediaEncodingProfile);

    if (result.Status != AudioFileNodeCreationStatus.Success)
    {
        // FileOutputNode creation failed
        ShowErrorMessage(result.Status.ToString());
        return;
    }

    fileOutputNode = result.FileOutputNode;
}

Node input bingkai audio

Simpul input bingkai audio memungkinkan Anda mendorong data audio yang Anda hasilkan dalam kode Anda sendiri ke grafik audio. Ini memungkinkan skenario seperti membuat synthesizer perangkat lunak kustom. Buat AudioFrameInputNode dengan memanggil CreateFrameInputNode.

AudioFrameInputNode frameInputNode;
private void CreateFrameInputNode()
{
    // Create the FrameInputNode at the same format as the graph, except explicitly set mono.
    AudioEncodingProperties nodeEncodingProperties = audioGraph.EncodingProperties;
    nodeEncodingProperties.ChannelCount = 1;
    frameInputNode = audioGraph.CreateFrameInputNode(nodeEncodingProperties);

    // Initialize the Frame Input Node in the stopped state
    frameInputNode.Stop();

    // Hook up an event handler so we can start generating samples when needed
    // This event is triggered when the node is required to provide data
    frameInputNode.QuantumStarted += node_QuantumStarted;
}

Peristiwa FrameInputNode.QuantumStarted dinaikkan ketika grafik audio siap untuk mulai memproses kuantum data audio berikutnya. Anda menyediakan data audio kustom yang dihasilkan dari dalam handler ke kejadian ini.

private void node_QuantumStarted(AudioFrameInputNode sender, FrameInputNodeQuantumStartedEventArgs args)
{
    // GenerateAudioData can provide PCM audio data by directly synthesizing it or reading from a file.
    // Need to know how many samples are required. In this case, the node is running at the same rate as the rest of the graph
    // For minimum latency, only provide the required amount of samples. Extra samples will introduce additional latency.
    uint numSamplesNeeded = (uint)args.RequiredSamples;

    if (numSamplesNeeded != 0)
    {
        AudioFrame audioData = GenerateAudioData(numSamplesNeeded);
        frameInputNode.AddFrame(audioData);
    }
}
  • Objek FrameInputNodeQuantumStartedEventArgs yang diteruskan ke penanganan aktivitas QuantumStarted mengekspos properti RequiredSamples yang menunjukkan berapa banyak sampel grafik audio yang perlu mengisi kuantum yang akan diproses.
  • Panggil AudioFrameInputNode.AddFrame untuk meneruskan objek AudioFrame yang diisi dengan data audio ke dalam grafik.
  • Sekumpulan API baru untuk menggunakan MediaFrameReader dengan data audio diperkenalkan di Windows 10, versi 1803. API ini memungkinkan Anda untuk mendapatkan objek AudioFrame dari sumber bingkai media, yang dapat diteruskan ke FrameInputNode menggunakan metode AddFrame . Untuk informasi selengkapnya, lihat Memproses bingkai audio dengan MediaFrameReader.
  • Contoh implementasi metode pembantu GenerateAudioData ditunjukkan di bawah ini.

Untuk mengisi AudioFrame dengan data audio, Anda harus mendapatkan akses ke buffer memori yang mendasar dari bingkai audio. Untuk melakukan ini, Anda harus menginisialisasi antarmuka COM IMemoryBufferByteAccess com dengan menambahkan kode berikut dalam namespace Anda.

[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

Kode berikut menunjukkan contoh implementasi metode pembantu GenerateAudioData yang membuat AudioFrame dan mengisinya dengan data audio.

private double audioWaveTheta = 0;

unsafe private AudioFrame GenerateAudioData(uint samples)
{
    // Buffer size is (number of samples) * (size of each sample)
    // We choose to generate single channel (mono) audio. For multi-channel, multiply by number of channels
    uint bufferSize = samples * sizeof(float);
    AudioFrame frame = new Windows.Media.AudioFrame(bufferSize);

    using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Write))
    using (IMemoryBufferReference reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;
        float* dataInFloat;

        // Get the buffer from the AudioFrame
        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacityInBytes);

        // Cast to float since the data we are generating is float
        dataInFloat = (float*)dataInBytes;

        float freq = 1000; // choosing to generate frequency of 1kHz
        float amplitude = 0.3f;
        int sampleRate = (int)audioGraph.EncodingProperties.SampleRate;
        double sampleIncrement = (freq * (Math.PI * 2)) / sampleRate;

        // Generate a 1kHz sine wave and populate the values in the memory buffer
        for (int i = 0; i < samples; i++)
        {
            double sinValue = amplitude * Math.Sin(audioWaveTheta);
            dataInFloat[i] = (float)sinValue;
            audioWaveTheta += sampleIncrement;
        }
    }

    return frame;
}
  • Karena metode ini mengakses buffer mentah yang mendasari jenis Windows Runtime, metode ini harus dinyatakan menggunakan kata kunci yang tidak aman . Anda juga harus mengonfigurasi proyek di Microsoft Visual Studio untuk mengizinkan kompilasi kode tidak aman dengan membuka halaman Properti proyek, mengklik halaman properti Build, dan memilih kotak centang Izinkan Kode Tidak Aman.
  • Inisialisasi instans baru AudioFrame, di namespace Windows.Media, dengan meneruskan ukuran buffer yang diinginkan ke konstruktor. Ukuran buffer adalah jumlah sampel yang dikalikan dengan ukuran setiap sampel.
  • Dapatkan AudioBuffer bingkai audio dengan memanggil LockBuffer.
  • Dapatkan instans antarmuka IMemoryBufferByteAccess COM dari buffer audio dengan memanggil CreateReference.
  • Dapatkan pointer ke data buffer audio mentah dengan memanggil IMemoryBufferByteAccess.GetBuffer dan transmisikan ke jenis data sampel data audio.
  • Isi buffer dengan data dan kembalikan AudioFrame untuk pengiriman ke grafik audio.

Simpul output bingkai audio

Simpul output bingkai audio memungkinkan Anda menerima dan memproses output data audio dari grafik audio dengan kode kustom yang Anda buat. Contoh skenario untuk ini adalah melakukan analisis sinyal pada output audio. Buat AudioFrameOutputNode dengan memanggil CreateFrameOutputNode.

AudioFrameOutputNode frameOutputNode;
private void CreateFrameOutputNode()
{
    frameOutputNode = audioGraph.CreateFrameOutputNode();
    audioGraph.QuantumStarted += AudioGraph_QuantumStarted;
}

Peristiwa AudioGraph.QuantumStarted dinaikkan ketika grafik audio telah mulai memproses kuantum data audio. Anda dapat mengakses data audio dari dalam handler untuk kejadian ini.

Catatan

Jika Anda ingin mengambil bingkai audio pada irama biasa, disinkronkan dengan grafik audio, panggil AudioFrameOutputNode.GetFrame dari dalam penanganan aktivitas QuantumStarted sinkron. Peristiwa QuantumProcessed dimunculkan secara asinkron setelah mesin audio menyelesaikan pemrosesan audio, yang berarti iramanya mungkin tidak teratur. Oleh karena itu, Anda tidak boleh menggunakan peristiwa QuantumProcessed untuk pemrosesan data bingkai audio yang disinkronkan.

private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
{
    AudioFrame frame = frameOutputNode.GetFrame();
    ProcessFrameOutput(frame);

}
  • Panggil GetFrame untuk mendapatkan objek AudioFrame yang diisi dengan data audio dari grafik.
  • Contoh implementasi metode pembantu ProcessFrameOutput ditunjukkan di bawah ini.
unsafe private void ProcessFrameOutput(AudioFrame frame)
{
    using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Write))
    using (IMemoryBufferReference reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;
        float* dataInFloat;

        // Get the buffer from the AudioFrame
        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacityInBytes);

        dataInFloat = (float*)dataInBytes;
    }
}
  • Seperti contoh node input bingkai audio di atas, Anda harus mendeklarasikan antarmuka IMemoryBufferByteAccess COM dan mengonfigurasi proyek Anda untuk memungkinkan kode yang tidak aman untuk mengakses buffer audio yang mendasar .
  • Dapatkan AudioBuffer bingkai audio dengan memanggil LockBuffer.
  • Dapatkan instans antarmuka IMemoryBufferByteAccess COM dari buffer audio dengan memanggil CreateReference.
  • Dapatkan pointer ke data buffer audio mentah dengan memanggil IMemoryBufferByteAccess.GetBuffer dan transmisikan ke jenis data sampel data audio.

Koneksi node dan simpul submix

Semua jenis simpul input mengekspos metode AddOutgoingConnection yang merutekan audio yang dihasilkan oleh simpul ke simpul yang diteruskan ke metode . Contoh berikut menghubungkan AudioFileInputNode ke AudioDeviceOutputNode, yang merupakan penyiapan sederhana untuk memutar file audio di speaker perangkat.

fileInputNode.AddOutgoingConnection(deviceOutputNode);

Anda dapat membuat lebih dari satu koneksi dari simpul input ke simpul lain. Contoh berikut menambahkan koneksi lain dari AudioFileInputNode ke AudioFileOutputNode. Sekarang, audio dari file audio diputar ke speaker perangkat dan juga ditulis ke file audio.

fileInputNode.AddOutgoingConnection(fileOutputNode);

Simpul output juga dapat menerima lebih dari satu koneksi dari simpul lain. Dalam contoh berikut, koneksi dibuat dari AudioDeviceInputNode ke simpul AudioDeviceOutput. Karena simpul output memiliki koneksi dari simpul input file dan node input perangkat, output akan berisi campuran audio dari kedua sumber. AddOutgoingConnection menyediakan kelebihan beban yang memungkinkan Anda menentukan nilai keuntungan untuk sinyal yang melewati koneksi.

deviceInputNode.AddOutgoingConnection(deviceOutputNode, .5);

Meskipun simpul output dapat menerima koneksi dari beberapa simpul, Anda mungkin ingin membuat campuran sinyal perantara dari satu atau beberapa simpul sebelum meneruskan campuran ke output. Misalnya, Anda mungkin ingin mengatur tingkat atau menerapkan efek ke subset sinyal audio dalam grafik. Untuk melakukan ini, gunakan AudioSubmixNode. Anda dapat terhubung ke simpul submix dari satu atau beberapa simpul input atau simpul submix lainnya. Dalam contoh berikut, simpul submix baru dibuat dengan AudioGraph.CreateSubmixNode. Kemudian, koneksi ditambahkan dari simpul input file dan node output bingkai ke simpul submix. Akhirnya, simpul submix terhubung ke simpul output file.

private void CreateSubmixNode()
{
    AudioSubmixNode submixNode = audioGraph.CreateSubmixNode();
    fileInputNode.AddOutgoingConnection(submixNode);
    frameInputNode.AddOutgoingConnection(submixNode);
    submixNode.AddOutgoingConnection(fileOutputNode);
}

Memulai dan menghentikan simpul grafik audio

Saat AudioGraph.Start dipanggil, grafik audio mulai memproses data audio. Setiap jenis node menyediakan metode Mulai dan Hentikan yang menyebabkan node individual memulai atau berhenti memproses data. Ketika AudioGraph.Stop dipanggil, semua pemrosesan audio di semua simpul dihentikan terlepas dari status simpul individual, tetapi status setiap simpul dapat diatur saat grafik audio dihentikan. Misalnya, Anda dapat memanggil Hentikan pada simpul individual saat grafik dihentikan lalu memanggil AudioGraph.Start, dan simpul individual akan tetap dalam status berhenti.

Semua jenis node mengekspos properti ConsumeInput yang, ketika diatur ke false, memungkinkan node untuk melanjutkan pemrosesan audio tetapi menghentikannya untuk mengonsumsi data audio apa pun yang dimasukkan dari simpul lain.

Semua jenis node mengekspos metode Reset yang menyebabkan node membuang data audio apa pun yang saat ini ada di buffernya.

Menambahkan efek audio

API grafik audio memungkinkan Anda menambahkan efek audio ke setiap jenis simpul dalam grafik. Simpul output, simpul input, dan simpul submiks masing-masing dapat memiliki jumlah efek audio yang tidak terbatas, hanya dibatasi oleh kemampuan perangkat keras. Contoh berikut menunjukkan penambahan efek gema bawaan ke simpul submix.

EchoEffectDefinition echoEffect = new EchoEffectDefinition(audioGraph);
echoEffect.Delay = 1000.0;
echoEffect.Feedback = .2;
echoEffect.WetDryMix = .5;

submixNode.EffectDefinitions.Add(echoEffect);
  • Semua efek audio mengimplementasikan IAudioEffectDefinition. Setiap simpul mengekspos properti EffectDefinitions yang mewakili daftar efek yang diterapkan pada simpul tersebut. Tambahkan efek dengan menambahkan objek definisinya ke daftar.
  • Ada beberapa kelas definisi efek yang disediakan di namespace Windows.Media.Audio . Ini termasuk:
  • Anda dapat membuat efek audio Anda sendiri yang mengimplementasikan IAudioEffectDefinition dan menerapkannya ke node apa pun dalam grafik audio.
  • Setiap jenis node mengekspos metode DisableEffectsByDefinition yang menonaktifkan semua efek dalam daftar EffectDefinitions node yang ditambahkan menggunakan definisi yang ditentukan. EnableEffectsByDefinition memungkinkan efek dengan definisi yang ditentukan.

Audio spasial

Dimulai dengan Windows 10, versi 1607, AudioGraph mendukung audio spasial, yang memungkinkan Anda menentukan lokasi di ruang 3D tempat audio dari input atau simpul submix dipancarkan. Anda juga dapat menentukan bentuk dan arah di mana audio dipancarkan, kecepatan yang akan digunakan untuk Doppler menggeser audio simpul, dan menentukan model pembusukan yang menjelaskan bagaimana audio dilemahkan dengan jarak.

Untuk membuat emiter, Anda dapat terlebih dahulu membuat bentuk di mana suara diproyeksikan dari emiter, yang dapat berupa kerucut atau omnidirectional. Kelas AudioNodeEmitterShape menyediakan metode statis untuk membuat masing-masing bentuk ini. Selanjutnya, buat model pembatas. Ini mendefinisikan bagaimana volume audio dari emiter menurun saat jarak dari pendengar meningkat. Metode CreateNatural membuat model pembbusukan yang meniru pembbusukan alami suara menggunakan model falloff kuadrat jarak. Terakhir, buat objek AudioNodeEmitterSettings. Saat ini, objek ini hanya digunakan untuk mengaktifkan dan menonaktifkan redaman Doppler berbasis kecepatan audio pemancar. Panggil konstruktor AudioNodeEmitter, melewati objek inisialisasi yang baru saja Anda buat. Secara default, emiter ditempatkan di asal, tetapi Anda dapat mengatur posisi emiter dengan properti Posisi.

Catatan

Pemancar simpul audio hanya dapat memproses audio yang diformat dalam mono dengan laju sampel 48kHz. Mencoba menggunakan audio atau audio stereo dengan laju sampel yang berbeda akan menghasilkan pengecualian.

Anda menetapkan pemancar ke simpul audio saat membuatnya dengan menggunakan metode pembuatan kelebihan beban untuk jenis simpul yang Anda inginkan. Dalam contoh ini, CreateFileInputNodeAsync digunakan untuk membuat simpul input file dari file tertentu dan objek AudioNodeEmitter yang ingin Anda kaitkan dengan simpul.

var emitterShape = AudioNodeEmitterShape.CreateOmnidirectional();
var decayModel = AudioNodeEmitterDecayModel.CreateNatural(.1, 1, 10, 100);
var settings = AudioNodeEmitterSettings.None;

var emitter = new AudioNodeEmitter(emitterShape, decayModel, settings);
emitter.Position = new System.Numerics.Vector3(10, 0, 5);

CreateAudioFileInputNodeResult result = await audioGraph.CreateFileInputNodeAsync(file, emitter);

if (result.Status != AudioFileNodeCreationStatus.Success)
{
    ShowErrorMessage(result.Status.ToString());
}

fileInputNode = result.FileInputNode;

AudioDeviceOutputNode yang menghasilkan audio dari grafik ke pengguna memiliki objek pendengar, diakses dengan properti Listener, yang mewakili lokasi, orientasi, dan kecepatan pengguna di ruang 3D. Posisi semua emiter dalam grafik relatif terhadap posisi dan orientasi objek pendengar. Secara default, listener terletak di asal (0,0,0) menghadap ke depan di sepanjang sumbu Z, tetapi Anda dapat mengatur posisi dan orientasinya dengan properti Posisi dan Orientasi.

deviceOutputNode.Listener.Position = new System.Numerics.Vector3(100, 0, 0);
deviceOutputNode.Listener.Orientation = System.Numerics.Quaternion.CreateFromYawPitchRoll(0, (float)Math.PI, 0);

Anda dapat memperbarui lokasi, kecepatan, dan arah emitor pada runtime untuk mensimulasikan pergerakan sumber audio melalui ruang 3D.

var emitter = fileInputNode.Emitter;
emitter.Position = newObjectPosition;
emitter.DopplerVelocity = newObjectPosition - oldObjectPosition;

Anda juga dapat memperbarui lokasi, kecepatan, dan orientasi objek pendengar pada runtime untuk mensimulasikan pergerakan pengguna melalui ruang 3D.

deviceOutputNode.Listener.Position = newUserPosition;

Secara default, audio spasial dihitung menggunakan algoritma fungsi transfer head-relative (HRTF) Microsoft untuk meredakan audio berdasarkan bentuk, kecepatan, dan posisinya relatif terhadap pendengar. Anda dapat mengatur properti SpatialAudioModel ke FoldDown untuk menggunakan metode campuran stereo sederhana untuk mensimulasikan audio spasial yang kurang akurat tetapi membutuhkan lebih sedikit sumber daya CPU dan memori.

Lihat juga