오디오 그래프

이 문서에서는 Windows.Media.Audio 네임스페이스의 API를 사용하여 오디오 라우팅, 믹싱 및 처리 시나리오에 대한 오디오 그래프를 만드는 방법을 설명합니다.

오디오 그래프는 오디오 데이터가 흐르는 상호 연결된 오디오 노드 집합입니다.

  • 오디오 입력 노드는 오디오 입력 장치, 오디오 파일 또는 사용자 지정 코드에서 그래프에 오디오 데이터를 제공합니다. lat

  • 오디오 출력 노드는 그래프에서 처리되는 오디오의 대상입니다. 오디오는 그래프에서 오디오 출력 디바이스, 오디오 파일 또는 사용자 지정 코드로 라우팅할 수 있습니다.

  • 서브믹스 노드는 하나 이상의 노드에서 오디오를 가져와 그래프의 다른 노드로 라우팅할 수 있는 단일 출력으로 결합합니다.

모든 노드가 만들어지고 노드 간의 연결이 설정되면 입력 노드에서 서브믹스 노드를 통해 출력 노드로 오디오 그래프와 오디오 데이터 흐름을 시작합니다. 이 모델은 디바이스의 마이크에서 오디오 파일로 녹음, 파일에서 디바이스 스피커로 오디오 재생 또는 여러 원본의 오디오를 빠르고 쉽게 구현하는 등의 시나리오를 만듭니다.

오디오 그래프에 오디오 효과를 추가하여 추가 시나리오를 사용할 수 있습니다. 오디오 그래프의 모든 노드는 노드를 통과하는 오디오에서 오디오 처리를 수행하는 0개 이상의 오디오 효과로 채워질 수 있습니다. 에코, 이퀄라이저, 리미팅, 리버브 등 몇 줄의 코드만으로 오디오 노드에 부착할 수 있는 여러 가지 기본 제공 이펙트가 있습니다. 기본 제공 효과와 정확히 동일한 방식으로 작동하는 사용자 지정 오디오 효과를 만들 수도 있습니다.

참고 항목

AudioGraph UWP 샘플은 이 개요에 설명된 코드를 구현합니다. 샘플을 다운로드하여 컨텍스트에서 코드를 보거나 사용자 고유의 앱의 시작점으로 사용할 수 있습니다.

Windows 런타임 AudioGraph 또는 XAudio2 선택

Windows 런타임 오디오 그래프 API는 COM 기반 XAudio2 API를 사용하여 구현할 수도 있는 기능을 제공합니다. 다음은 XAudio2와 다른 Windows 런타임 오디오 그래프 프레임워크의 기능입니다.

Windows 런타임 오디오 그래프 API:

  • XAudio2보다 훨씬 쉽게 사용할 수 있습니다.
  • C++에 대해 지원되는 것 외에도 C#에서 사용할 수 있습니다.
  • 압축된 파일 형식을 포함한 오디오 파일을 직접 사용할 수 있습니다. XAudio2는 오디오 버퍼에서만 작동하며 파일 I/O 기능을 제공하지 않습니다.
  • Windows 10에서 대기 시간이 짧은 오디오 파이프라인을 사용할 수 있습니다.
  • 기본 엔드포인트 매개 변수를 사용할 때 자동 엔드포인트 전환을 지원합니다. 예를 들어 사용자가 디바이스의 스피커에서 헤드셋으로 전환하면 오디오가 자동으로 새 입력으로 리디렉션됩니다.

AudioGraph 클래스

AudioGraph 클래스는 그래프를 구성하는 모든 노드의 부모입니다. 이 개체를 사용하여 모든 오디오 노드 유형의 인스턴스를 만듭니다. 그래프에 대한 구성 설정을 포함하는 AudioGraphSettings를 초기화한 다음 AudioGraph.CreateAsync를 호출하여 AudioGraph 클래스의 인스턴스를 만듭니다. 반환된 CreateAudioGraphResult는 생성된 오디오 그래프에 대한 액세스 권한을 부여하거나 오디오 그래프 만들기에 실패할 경우 오류 값을 제공합니다.

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;

}
  • 모든 오디오 노드 유형은 AudioGraph 클래스의 Create* 메서드를 사용하여 만들어집니다.

  • AudioGraph.Start 메서드를 사용하면 오디오 그래프가 오디오 데이터 처리를 시작합니다. AudioGraph.Stop 메서드는 오디오 처리를 중지합니다. 그래프가 실행되는 동안 그래프의 각 노드를 독립적으로 시작 및 중지할 수 있지만 그래프가 중지될 때는 노드가 활성화되지 않습니다. ResetAllNodes를 사용하면 그래프의 모든 노드는 현재 오디오 버퍼에 있는 모든 데이터를 삭제합니다.

  • QuantumStarted 이벤트는 그래프가 오디오 데이터의 새 양자 처리를 시작할 때 발생합니다. QuantumProcessed 이벤트는 양자 처리가 완료될 때 발생합니다.

  • 필요한 유일한 AudioGraphSettings 속성은 AudioRenderCategory입니다. 이 값을 지정하면 시스템에서 지정된 범주에 대한 오디오 파이프라인을 최적화할 수 있습니다.

  • 오디오 그래프의 양자 크기는 한 번에 처리되는 샘플 수를 결정합니다. 기본적으로 양자 크기는 기본 샘플 속도에 따라 10ms입니다. DesiredSamplesPerQuantum 속성을 설정하여 사용자 지정 양자 크기를 지정하는 경우 QuantumSizeSelectionMode 속성도 ClosestToDesired로 설정하거나 제공된 값이 무시됩니다. 이 값을 사용하는 경우 시스템은 사용자가 지정한 값에 최대한 가까운 양자 크기를 선택합니다. 실제 양자 크기를 확인하려면 AudioGraph를 만든 후 SamplesPerQuantum을 검사하세요.

  • 오디오 그래프를 파일과 함께 사용할 계획이며 오디오 디바이스로 출력할 계획이 없는 경우 DesiredSamplesPerQuantum 속성을 설정하지 않고 기본 양자 크기를 사용하는 것이 좋습니다.

  • DesiredRenderDeviceAudioProcessing 속성은 오디오 그래프의 출력에서 기본 렌더링 디바이스가 수행하는 처리량을 결정합니다. 기본 설정을 사용하면 시스템에서 지정된 오디오 렌더링 범주에 대한 기본 오디오 처리를 사용할 수 있습니다. 이 처리는 일부 장치, 특히 작은 스피커가 있는 모바일 장치에서 오디오의 소리를 크게 향상시킬 수 있습니다. 원시 설정은 수행되는 신호 처리 양을 최소화하여 성능을 향상시킬 수 있지만 일부 장치에서는 음질이 열등할 수 있습니다.

  • QuantumSizeSelectionModeLowestLatency로 설정된 경우 오디오 그래프는 DesiredRenderDeviceAudioProcessing원시를 자동으로 사용합니다.

  • Windows 10, 버전 1803부터 AudioGraphSettings.MaxPlaybackSpeedFactor 속성을 설정하여 AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactorMediaSourceInputNode.PlaybackSpeedFactor 속성에 사용되는 최댓값을 설정할 수 있습니다. 오디오 그래프가 1보다 큰 재생 속도 비율을 지원하면 시스템은 충분한 오디오 데이터 버퍼를 유지하기 위해 추가 메모리를 할당해야 합니다. 그렇기 때문에 MaxPlaybackSpeedFactor를 앱에 필요한 최솟값으로 설정하면 앱의 메모리 사용량이 감소합니다. 앱이 정상 속도로만 콘텐츠를 재생할 경우 MaxPlaybackSpeedFactor를 1로 설정하는 것이 좋습니다.

  • EncodingProperties는 그래프에서 사용하는 오디오 형식을 결정합니다. 32비트 float 형식만 지원됩니다.

  • PrimaryRenderDevice는 오디오 그래프의 기본 렌더링 디바이스를 설정합니다. 설정하지 않으면 기본 시스템 디바이스가 사용됩니다. 기본 렌더링 디바이스는 그래프의 다른 노드에 대한 양자 크기를 계산하는 데 사용됩니다. 시스템에 오디오 렌더링 디바이스가 없으면 오디오 그래프를 만들지 못합니다.

오디오 그래프에서 기본 오디오 렌더링 장치를 사용하도록 하거나 Windows.Devices.Enumeration.DeviceInformation 클래스를 사용하여 FindAllAsync를 호출하고 Windows.Media.Devices.MediaDevice.GetAudioRenderSelector에서 반환된 오디오 렌더링 장치 선택기를 전달하여 시스템의 사용 가능한 오디오 렌더링 장치 목록을 가져올 수 있습니다. 반환된 DeviceInformation 개체 중 하나를 프로그래밍 방식으로 선택하거나 UI를 표시하여 사용자가 디바이스를 선택한 다음 이를 사용하여 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;

디바이스 입력 노드

디바이스 입력 노드는 마이크와 같이 시스템에 연결된 오디오 캡처 디바이스에서 오디오를 그래프로 공급합니다. CreateDeviceInputNodeAsync를 호출하여 시스템의 기본 오디오 캡처 디바이스를 사용하는 DeviceInputNode 개체를 만듭니다. 시스템에서 지정된 범주에 대한 오디오 파이프라인을 최적화할 수 있도록 AudioRenderCategory를 제공합니다.

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;
}

디바이스 입력 노드에 대해 특정 오디오 캡처 디바이스를 지정하려는 경우 Windows.Devices.Enumeration.DeviceInformation 클래스를 통해 FindAllAsync를 호출하고 Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector에서 반환된 오디오 렌더 디바이스 선택기를 제공하여 시스템의 사용 가능한 오디오 캡처 디바이스 목록을 가져올 수 있습니다. 반환된 DeviceInformation 개체 중 하나를 프로그래밍 방식으로 선택하거나 사용자가 디바이스를 선택한 다음 CreateDeviceInputNodeAsync에 전달할 수 있도록 UI를 표시할 수 있습니다.

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);

디바이스 출력 노드

디바이스 출력 노드는 그래프의 오디오를 스피커 또는 헤드셋과 같은 오디오 렌더링 디바이스로 푸시합니다. CreateDeviceOutputNodeAsync를 호출하여 DeviceOutputNode를 만듭니다. 출력 노드는 오디오 그래프의 PrimaryRenderDevice를 사용합니다.

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;
}

파일 입력 노드

파일 입력 노드를 사용하면 오디오 파일의 데이터를 그래프로 공급할 수 있습니다. CreateFileInputNodeAsync를 호출하여 AudioFileInputNode를 만듭니다.

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;
}
  • 파일 입력 노드는 mp3, wav, wma, m4a 파일 형식을 지원합니다.
  • StartTime 속성을 설정하여 재생이 시작되는 파일에 시간 오프셋을 지정합니다. 이 속성이 null이면 파일의 시작이 사용됩니다. EndTime 속성을 설정하여 재생이 끝나는 파일에 시간 오프셋을 지정합니다. 이 속성이 null이면 파일의 끝이 사용됩니다. 시작 시간 값은 종료 시간 값보다 낮아야 하며, 종료 시간 값은 오디오 파일의 기간보다 작거나 같아야 하며 Duration 속성 값을 검사하여 결정될 수 있습니다.
  • Seek를 호출하고 재생 위치를 이동할 파일에 시간 오프셋을 지정하여 오디오 파일의 위치를 찾습니다. 지정된 값은 StartTimeEndTime 범위 내에 있어야 합니다. 읽기 전용 Position 속성을 사용하여 노드의 현재 재생 위치를 가져옵니다.
  • LoopCount 속성을 설정하여 오디오 파일의 루프를 사용하도록 설정합니다. null이 아닌 경우 이 값은 초기 재생 후 파일이 재생되는 횟수를 나타냅니다. 예를 들어 LoopCount를 1로 설정하면 파일이 총 2번 재생되고 5로 설정하면 파일이 총 6번 재생됩니다. LoopCount를 null로 설정하면 파일이 무기한 반복됩니다. 루프를 중지하려면 값을 0으로 설정합니다.
  • PlaybackSpeedFactor를 설정하여 오디오 파일이 재생되는 속도를 조정합니다. 값 1은 파일의 원래 속도를 나타내고.5는 절반 속도이고 2는 이중 속도입니다.

MediaSource 입력 노드

MediaSource 클래스는 다양한 소스에서 미디어를 참조하는 일반적인 방법을 제공하며 디스크, 스트림 또는 적응형 스트리밍 네트워크 소스의 파일일 수 있는 기본 미디어 형식에 관계없이 미디어 데이터에 액세스하기 위한 공통 모델을 공개합니다. **MediaSourceAudioInputNode 노드를 사용하여 MediaSource의 오디오 데이터를 오디오 그래프로 보낼 수 있습니다. CreateMediaSourceAudioInputNodeAsync를 호출하여 MediaSourceAudioInputNode를 만들고 재생할 콘텐츠를 대표하는 MediaSource 개체를 전달합니다. 작동 상태를 결정하기 위해 사용할 수 있는 **CreateMediaSourceAudioInputNodeResult를 반환하기 위해 Status 속성을 확인합니다. 상태가 Success인 경우 만들어진 MediaSourceAudioInputNode를 가져오려면 Node 속성에 액세스합니다. 네트워크를 통해 콘텐츠 스트리밍을 나타내는 AdaptiveMediaSource 개체에서 노드를 생성하는 예제는 다음과 같습니다. MediaSource 작업에 대한 자세한 내용은 미디어 항목, 재생 목록 및 트랙을 참조하세요. 인터넷을 통한 스트리밍 미디어 콘텐츠에 대한 자세한 내용은 적응 스트리밍을 참조하세요.

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;
}

MediaSource 콘텐츠 재생이 끝날 때 알림을 받으려면 MediaSourceCompleted 이벤트를 위한 처리기에 등록합니다.

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

디스크에서의 파일 재생이 항상 성공적으로 완료될 가능성이 높지만 네트워크 연결이 변경되거나 오디오 그래프가 제어할 수 없는 기타 문제로 인해 재생 중에 네트워크 소스에서 스트리밍된 미디어가 재생되지 않을 수 있습니다. 재생 중에 MediaSource를 재생할 수 없게 되면 오디오 그래프는 UnrecoverableErrorOccurred 이벤트를 발생시킵니다. 이 이벤트에 대한 처리기를 사용하여 오디오 그래프를 중지하고 처리한 다음, 그래프를 다시 초기화할 수 있습니다.

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();
    }
}

파일 출력 노드

파일 출력 노드를 사용하면 그래프의 오디오 데이터를 오디오 파일로 보낼 수 있습니다. CreateFileOutputNodeAsync를 호출하여 AudioFileOutputNode를 만듭니다.

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;
}

오디오 프레임 입력 노드

오디오 프레임 입력 노드를 사용하면 자체 코드에서 생성한 오디오 데이터를 오디오 그래프로 푸시할 수 있습니다. 이렇게 하면 사용자 지정 소프트웨어 신시사이저를 만드는 것과 같은 시나리오를 사용할 수 있습니다. CreateFrameInputNode를 호출하여 AudioFrameInputNode를 만듭니다.

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;
}

FrameInputNode.QuantumStarted 이벤트는 오디오 그래프가 오디오 데이터의 다음 퀀텀 처리를 시작할 준비가 되면 발생합니다. 처리기 내에서 생성된 사용자 지정 오디오 데이터를 이 이벤트로 제공합니다.

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);
    }
}
  • QuantumStarted 이벤트 처리기에 전달된 FrameInputNodeQuantumStartedEventArgs 개체는 오디오 그래프가 처리할 양자를 채우는 데 필요한 샘플 수를 나타내는 RequiredSamples 속성을 노출합니다.
  • AudioFrameInputNode.AddFrame을 호출하여 오디오 데이터로 채워진 AudioFrame 개체를 그래프에 전달합니다.
  • 오디오 데이터가 포함된 MediaFrameReader를 사용하기 위한 새로운 API 세트가 Windows 10, 1803 버전에 도입되었습니다. 이러한 API를 통해 AddFrame 메서드를 사용하여 FrameInputNode로 전달할 수 있는 미디어 프레임 소스에서 AudioFrame 개체를 얻을 수 있습니다. 자세한 내용은 MediaFrameReader를 사용하여 오디오 프레임 처리를 참조하세요.
  • GenerateAudioData 도우미 메서드의 예제 구현은 다음과 같습니다.

AudioFrame을 오디오 데이터로 채려면 오디오 프레임의 기본 메모리 버퍼에 액세스해야 합니다. 이렇게 하려면 네임스페이스 내에 다음 코드를 추가하여 IMemoryBufferByteAccess COM 인터페이스를 초기화해야 합니다.

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

다음 코드에서는 AudioFrame을 만들고 오디오 데이터로 채우는 GenerateAudioData 도우미 메서드의 예제 구현을 보여 주었습니다.

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;
}
  • 이 메서드는 Windows 런타임 형식의 기반이 되는 원시 버퍼에 액세스하므로 안전하지 않은 키워드(keyword)를 사용하여 선언해야 합니다. 또한 프로젝트의 속성 페이지를 열고 빌드 속성 페이지를 클릭하고 안전하지 않은 코드 허용 검사 상자를 선택하여 안전하지 않은 코드를 컴파일할 수 있도록 Microsoft Visual Studio에서 프로젝트를 구성해야 합니다.
  • 원하는 버퍼 크기를 생성자에 전달하여 Windows.Media 네임스페이스에서 AudioFrame의 새 인스턴스를 초기화합니다. 버퍼 크기는 샘플 수에 각 샘플의 크기를 곱한 값입니다.
  • LockBuffer를 호출하여 오디오 프레임의 AudioBuffer를 가져옵니다.
  • CreateReference를 호출하여 오디오 버퍼에서 IMemoryBufferByteAccess COM 인터페이스의 인스턴스를 가져옵니다.
  • IMemoryBufferByteAccess.GetBuffer를 호출하여 원시 오디오 버퍼 데이터에 대한 포인터를 가져와서 오디오 데이터의 샘플 데이터 형식으로 캐스팅합니다.
  • 데이터로 버퍼를 채우고 오디오 그래프에 제출하기 위해 AudioFrame을 반환합니다.

오디오 프레임 출력 노드

오디오 프레임 출력 노드를 사용하면 사용자가 만든 사용자 지정 코드를 사용하여 오디오 그래프에서 오디오 데이터 출력을 수신하고 처리할 수 있습니다. 이에 대한 예제 시나리오는 오디오 출력에 대한 신호 분석을 수행하는 것입니다. CreateFrameOutputNode를 호출하여 AudioFrameOutputNode를 만듭니다.

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

오디오 그래프가 오디오 데이터의 퀀텀 처리를 시작하면 AudioGraph.QuantumStarted 이벤트가 발생합니다. 이 이벤트에 대한 처리기 내에서 오디오 데이터에 액세스할 수 있습니다.

참고 항목

오디오 그래프와 동기화를 유지하며 규칙적인 흐름에 따라 오디오 프레임을 검색하려면 동시 QuantumStarted 이벤트 처리기 내에서 AudioFrameOutputNode.GetFrame을 호출합니다. QuantumProcessed 이벤트는 오디오 엔진이 오디오 처리를 완료함과 동시에 발생하므로 흐름이 불규칙적일 수 있습니다. 따라서 오디오 프레임 데이터 처리를 동기화하려면 QuantumProcessed 이벤트를 사용해서는 안 됩니다.

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

}
  • GetFrame을 호출하여 그래프의 오디오 데이터로 채워진 AudioFrame 개체를 가져옵니다.
  • ProcessFrameOutput 도우미 메서드의 예제 구현은 다음과 같습니다.
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;
    }
}
  • 위의 오디오 프레임 입력 노드 예제와 마찬가지로 IMemoryBufferByteAccess COM 인터페이스를 선언하고 기본 오디오 버퍼에 액세스하기 위해 안전하지 않은 코드를 허용하도록 프로젝트를 구성해야 합니다.
  • LockBuffer를 호출하여 오디오 프레임의 AudioBuffer를 가져옵니다.
  • CreateReference를 호출하여 오디오 버퍼에서 IMemoryBufferByteAccess COM 인터페이스의 인스턴스를 가져옵니다.
  • IMemoryBufferByteAccess.GetBuffer를 호출하여 원시 오디오 버퍼 데이터에 대한 포인터를 가져와서 오디오 데이터의 샘플 데이터 형식으로 캐스팅합니다.

노드 연결 및 서브믹스 노드

모든 입력 노드 형식은 노드에서 생성된 오디오를 메서드로 전달되는 노드로 라우팅하는 AddOutgoingConnection 메서드를 노출합니다. 다음 예제에서는 AudioFileInputNodeAudioDeviceOutputNode에 연결합니다. 이 설정은 디바이스의 스피커에서 오디오 파일을 재생하기 위한 간단한 설정입니다.

fileInputNode.AddOutgoingConnection(deviceOutputNode);

입력 노드에서 다른 노드로의 연결을 둘 이상 만들 수 있습니다. 다음 예제에서는 AudioFileOutputNode의 다른 연결을 AudioFileInputNode에 추가합니다. 이제 오디오 파일의 오디오가 디바이스의 스피커로 재생되고 오디오 파일에도 기록됩니다.

fileInputNode.AddOutgoingConnection(fileOutputNode);

출력 노드는 다른 노드에서 둘 이상의 연결을 받을 수도 있습니다. 다음 예제에서는 AudioDeviceInputNode에서 AudioDeviceOutput 노드로 연결됩니다. 출력 노드에는 파일 입력 노드와 디바이스 입력 노드의 연결이 있으므로 출력에는 두 원본의 오디오가 혼합되어 포함됩니다. AddOutgoingConnection은 연결을 통과하는 신호에 대한 게인 값을 지정할 수 있는 오버로드를 제공합니다.

deviceInputNode.AddOutgoingConnection(deviceOutputNode, .5);

출력 노드는 여러 노드의 연결을 허용할 수 있지만 혼합을 출력에 전달하기 전에 하나 이상의 노드에서 신호의 중간 혼합을 만들 수 있습니다. 예를 들어 그래프에서 오디오 신호의 하위 집합에 수준을 설정하거나 효과를 적용할 수 있습니다. 이렇게 하려면 AudioSubmixNode를 사용합니다. 하나 이상의 입력 노드 또는 다른 서브믹스 노드에서 서브믹스 노드에 연결할 수 있습니다. 다음 예제에서는 AudioGraph.CreateSubmixNode를 사용하여 새 서브믹스 노드를 만듭니다. 그런 다음 파일 입력 노드와 프레임 출력 노드에서 서브믹스 노드로 연결이 추가됩니다. 마지막으로, 서브믹스 노드가 파일 출력 노드에 연결됩니다.

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

오디오 그래프 노드 시작 및 중지

AudioGraph.Start가 호출되면 오디오 그래프가 오디오 데이터 처리를 시작합니다. 모든 노드 유형은 개별 노드가 데이터 처리를 시작하거나 중지하도록 하는 StartStop 메서드를 제공합니다. AudioGraph.Stop이 호출되면 개별 노드의 상태에 관계없이 모든 노드의 모든 오디오 처리가 중지되지만 오디오 그래프가 중지되는 동안 각 노드의 상태를 설정할 수 있습니다. 예를 들어 그래프가 중지된 동안 개별 노드에서 Stop을 호출한 다음 AudioGraph.Start를 호출하면 개별 노드가 중지된 상태로 유지됩니다.

모든 노드 유형은 False로 설정하면 노드가 오디오 처리를 계속할 수 있지만 다른 노드에서 입력되는 오디오 데이터를 사용하지 못하게 하는 ConsumeInput 속성을 노출합니다.

모든 노드 형식은 현재 버퍼에 있는 오디오 데이터를 카드 노드를 해제하는 Reset 메서드를 노출합니다.

오디오 효과 추가

오디오 그래프 API를 사용하면 그래프의 모든 노드 유형에 오디오 효과를 추가할 수 있습니다. 출력 노드, 입력 노드 및 서브믹스 노드는 각각 하드웨어의 기능에 의해서만 제한되는 무제한의 오디오 효과를 가질 수 있습니다. 다음 예제에서는 서브믹스 노드에 기본 제공 에코 효과를 추가하는 방법을 보여 줍니다.

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

submixNode.EffectDefinitions.Add(echoEffect);
  • 모든 오디오 효과는 IAudioEffectDefinition을 구현합니다. 모든 노드는 해당 노드에 적용된 효과 목록을 나타내는 EffectDefinitions 속성을 노출합니다. 정의 개체를 목록에 추가하여 효과를 추가합니다.
  • Windows.Media.Audio 네임스페이스에 제공되는 여러 효과 정의 클래스가 있습니다. 이러한 개체는 다음과 같습니다.
  • IAudioEffectDefinition을 구현하는 고유한 오디오 효과를 만들고 오디오 그래프의 모든 노드에 적용할 수 있습니다.
  • 모든 노드 형식은 지정된 정의를 사용하여 추가된 노드의 EffectDefinitions 목록에 있는 모든 효과를 사용하지 않도록 설정하는 DisableEffectsByDefinition 메서드를 노출합니다. EnableEffectsByDefinition을 사용하면 지정된 정의를 사용하여 효과를 사용할 수 있습니다.

공간 오디오

Windows 10 버전 1607부터 AudioGraph는 공간 오디오를 지원하므로 입력 또는 서브믹스 노드의 오디오가 내보내지는 3D 공간의 위치를 지정할 수 있습니다. 오디오가 내보내지는 모양과 방향, 노드의 오디오를 Doppler 이동에 사용할 속도, 그리고 오디오가 거리와 함께 감쇠되는 방식을 설명하는 감쇠 모델을 정의할 수도 있습니다.

방사체를 만들려면 먼저 방사체에서 소리가 프로젝션되는 셰이프를 만들 수 있습니다. 이 모양은 원뿔 또는 전방향일 수 있습니다. AudioNodeEmitterShape 클래스는 이러한 각 셰이프를 만들기 위한 정적 메서드를 제공합니다. 다음으로, 감쇠 모델을 만듭니다. 수신기와의 거리가 늘어나면 이미터의 오디오 볼륨이 감소하는 방법을 정의합니다. CreateNatural 메서드는 거리 제곱 대체 모델을 사용하여 소리의 자연 감쇠를 에뮬레이트하는 감쇠 모델을 만듭니다. 마지막으로 AudioNodeEmitterSettings 설정 개체를 만듭니다. 현재 이 개체는 발광기 오디오의 속도 기반 Doppler 감쇠를 사용하거나 사용하지 않도록 설정하는 데만 사용됩니다. AudioNodeEmitter 생성자를 호출하여 방금 만든 초기화 개체를 전달합니다. 기본적으로 방출기는 원본에 배치되지만 Position 속성을 사용하여 방출기의 위치를 설정할 수 있습니다.

참고 항목

오디오 노드 내보내기는 샘플 속도가 48kHz인 모노 형식의 오디오만 처리할 수 있습니다. 다른 샘플 속도로 스테레오 오디오 또는 오디오를 사용하려고 하면 예외가 발생합니다.

원하는 노드 유형에 대해 오버로드된 생성 방법을 사용하여 방출기를 만들 때 오디오 노드에 방출기를 할당합니다. 이 예제에서는CreateFileInputNodeAsync를 사용하여 지정된 파일 및 노드와 연결하려는 AudioNodeEmitter 개체에서 파일 입력 노드를 만듭니다.

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에는 3D 공간에서 사용자의 위치, 방향 및 속도를 나타내는 수신기 속성으로 액세스되는 수신기 개체가 있습니다. 그래프에서 모든 송신기의 위치는 수신기 개체의 위치와 방향을 기준으로 합니다. 기본적으로 수신기는 Z 축을 따라 앞으로 향한 원점(0,0,0)에 있지만 PositionOrientation 속성을 사용하여 위치 및 방향을 설정할 수 있습니다.

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

런타임에 내보내기의 위치, 속도 및 방향을 업데이트하여 3D 공간을 통해 오디오 원본의 이동을 시뮬레이션할 수 있습니다.

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

런타임에 수신기 개체의 위치, 속도 및 방향을 업데이트하여 3D 공간을 통해 사용자의 이동을 시뮬레이션할 수도 있습니다.

deviceOutputNode.Listener.Position = newUserPosition;

기본적으로 공간 오디오는 Microsoft의 HRTF(헤드 상대 전송 함수) 알고리즘을 사용하여 수신기에 상대적인 모양, 속도 및 위치에 따라 오디오를 감쇠하기 위해 계산됩니다. SpatialAudioModel 속성을 FoldDown으로 설정하여 정확도가 낮지만 CPU 및 메모리 리소스가 덜 필요한 공간 오디오를 시뮬레이션하는 간단한 스테레오 혼합 방법을 사용할 수 있습니다.

참고 항목