Bluetooth 接続のリモート デバイスからオーディオ再生を有効にするEnable audio playback from remote Bluetooth-connected devices

この記事では、 Audioplaybackconnection を使用して、Bluetooth に接続されたリモートデバイスがローカルコンピューターでオーディオを再生できるようにする方法について説明します。This article shows you how to use AudioPlaybackConnection to enable Bluetooth-connected remote devices to play back audio on the local machine.

Windows 10 以降では、バージョン2004のリモートオーディオソースで Windows デバイスにオーディオをストリーミングできるため、PC を Bluetooth スピーカーのように動作させ、ユーザーが電話から音声を聞くことができるようにするなどのシナリオを実現できます。Starting with Windows 10, version 2004 remote audio sources can stream audio to Windows devices, enabling scenarios such as configuring a PC to behave like a Bluetooth speaker and allowing users to hear audio from their phone. 実装では、OS の Bluetooth コンポーネントを使用して受信オーディオデータを処理し、システムのシステムのオーディオエンドポイント (内蔵 PC スピーカーや有線ヘッドホンなど) で再生します。The implementation uses the Bluetooth components in the OS to process incoming audio data and play it on the system's audio endpoints on the system such as built-in PC speakers or wired headphones. 基になる Bluetooth A2DP sink の有効化は、システムではなく、エンドユーザーのシナリオを担当するアプリによって管理されます。The enabling of the underlying Bluetooth A2DP sink is managed by apps, which are responsible for the end-user scenario, rather than by the system.

Audioplaybackconnectionクラスは、リモートデバイスからの接続を有効または無効にしたり、接続を作成したりするために使用されます。これにより、リモートオーディオの再生を開始できます。The AudioPlaybackConnection class is used to enable and disable connections from a remote device as well as to create the connection, allowing remote audio playback to begin.

ユーザー インターフェイスの追加Add a user interface

この記事の例では、次の単純な XAML UI を使用します。ここでは、使用可能なリモートデバイスを表示する ListView コントロール、接続状態を表示する TextBlock 、および接続を有効、無効、および開くための3つのボタンを定義しています。For the examples in this article, we will use the following simple XAML UI which defines ListView control to display available remote devices, a TextBlock to display connection status, and three buttons for enabling, disabling, and opening connections.

<Grid x:Name="MainGrid" Loaded="MainGrid_Loaded">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="Connection state: "/>
        <TextBlock x:Name="ConnectionState" Grid.Row="0" Text="Disconnected."/>
    </StackPanel>
    <ListView x:Name="DeviceListView" ItemsSource="{x:Bind devices}" Grid.Row="1">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="enumeration:DeviceInformation">
                <StackPanel Orientation="Horizontal" Margin="6">
                    <SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
                    <StackPanel>
                        <TextBlock Text="{x:Bind Name}" FontWeight="Bold"/>
                    </StackPanel>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    <StackPanel Orientation="Vertical" Grid.Row="2">
        <Button x:Name="EnableAudioPlaybackConnectionButton" Content="Enable Audio Playback Connection" Click="EnableAudioPlaybackConnectionButton_Click"/>
        <Button x:Name="ReleaseAudioPlaybackConnectionButton" Content="Release Audio Playback Connection" Click="ReleaseAudioPlaybackConnectionButton_Click"/>
        <Button x:Name="OpenAudioPlaybackConnectionButtonButton" Content="Open Connection" Click="OpenAudioPlaybackConnectionButtonButton_Click" IsEnabled="False"/>
    </StackPanel>
     
</Grid>

DeviceWatcher を使用してリモートデバイスを監視するUse DeviceWatcher to monitor for remote devices

Devicewatcherクラスを使用すると、接続されているデバイスを検出できます。The DeviceWatcher class allows you to detect connected devices. Audioplaybackconnection. GetDeviceSelectorメソッドは、監視するデバイスの種類をデバイス監視に示す文字列を返します。The AudioPlaybackConnection.GetDeviceSelector method returns a string that tells the device watcher what kinds of devices to watch for. この文字列を Devicewatcher コンストラクターに渡します。Pass this string into the DeviceWatcher constructor.

デバイス監視が開始されたときに接続されているデバイスごとに、デバイス監視の実行中に接続されているデバイスごとに、 devicewatcher. Added イベントが発生します。The DeviceWatcher.Added event is raised for each device that is connected when the device watcher is started as well as for any device that is connected while the device watcher is running. 以前に接続されていたデバイスが切断されると、 Devicewatcher. 削除 イベントが発生します。The DeviceWatcher.Removed event is raised if a previously connected device disconnects.

Devicewatcher を呼び出します。開始して、オーディオ再生接続をサポートしている接続デバイスの監視を開始します。Call DeviceWatcher.Start to begin watching for connected devices that support audio playback connections. この例では、UI のメイン グリッド コントロールが読み込まれるときに、デバイスマネージャーを起動します。In this example we will start the device manager when the main Grid control in the UI is loaded. Devicewatcherの使用方法の詳細については、「デバイスの列挙」を参照してください。For more information on using DeviceWatcher, see Enumerate Devices.

private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
    audioPlaybackConnections = new Dictionary<string, AudioPlaybackConnection>();

    // Start watching for paired Bluetooth devices. 
    this.deviceWatcher = DeviceInformation.CreateWatcher(AudioPlaybackConnection.GetDeviceSelector());

    // Register event handlers before starting the watcher. 
    this.deviceWatcher.Added += this.DeviceWatcher_Added;
    this.deviceWatcher.Removed += this.DeviceWatcher_Removed;

    this.deviceWatcher.Start();
}

デバイスウォッチャーの 追加 イベントでは、検出された各デバイスは deviceinformation オブジェクトによって表されます。In the device watcher's Added event, each discovered device is represented by a DeviceInformation object. 検出された各デバイスを、UI の ListView コントロールにバインドされている観測可能なコレクションに追加します。Add each discovered device to an observable collection that is bound to the ListView control in the UI.

private ObservableCollection<Windows.Devices.Enumeration.DeviceInformation> devices =
    new ObservableCollection<Windows.Devices.Enumeration.DeviceInformation>();

private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
{
    // Collections bound to the UI are updated in the UI thread. 
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
    {
        this.devices.Add(deviceInfo);
    });
}

オーディオ再生接続の有効化と解放Enable and release audio playback connections

デバイスとの接続を開く前に、接続を有効にする必要があります。Before opening a connection with a device, the connection must be enabled. これは、リモートデバイスのオーディオを PC で再生する新しいアプリケーションがあることをシステムに通知しますが、接続が開かれるまでオーディオの再生は開始されません。これは後の手順で示されます。This informs the system that there is a new application that wants audio from the remote device to be played on the PC, but audio does not begin playing until the connection is opened, which is shown in a later step.

[ オーディオ再生接続を有効にする ] ボタンのクリックハンドラーで、 ListView コントロールで現在選択されているデバイスに関連付けられているデバイス ID を取得します。In the click handler for the Enable Audio Playback Connection button, get the device ID associated with the currently selected device in the ListView control. この例では、有効になっている Audioplaybackconnection オブジェクトのディクショナリを保持します。This example maintains a dictionary of AudioPlaybackConnection objects that have been enabled. このメソッドは、まず、選択したデバイスの辞書にエントリが存在するかどうかを確認します。This method first checks to see if there is already an entry in the dictionary for the selected device. 次に、メソッドは、 Trycreatefromidを呼び出して選択したデバイス ID を渡すことで、選択したデバイスに対してAudioplaybackconnectionを作成しようとします。Next, the method attempts to create an AudioPlaybackConnection for the selected device by calling TryCreateFromId and passing in the selected device ID.

接続が正常に作成された場合は、新しい Audioplaybackconnection オブジェクトをアプリのディクショナリに追加し、オブジェクトの StateChanged イベントのハンドラーを登録します。さらに、新しい接続が有効になったことをシステムに通知するには、startasync 呼び出します。If the connection is successfully created, add the new AudioPlaybackConnection object to the app's dictionary, register a handler for the object's StateChanged event, and callStartAsync to notify the system that the new connection is enabled.

private Dictionary<String, AudioPlaybackConnection> audioPlaybackConnections;
private async void EnableAudioPlaybackConnectionButton_Click(object sender, RoutedEventArgs e)
{
    if (! (DeviceListView.SelectedItem is null))
    {
        var selectedDeviceId = (DeviceListView.SelectedItem as DeviceInformation).Id;
        if (!this.audioPlaybackConnections.ContainsKey(selectedDeviceId))
        {
            // Create the audio playback connection from the selected device id and add it to the dictionary. 
            // This will result in allowing incoming connections from the remote device. 
            var playbackConnection = AudioPlaybackConnection.TryCreateFromId(selectedDeviceId);

            if (playbackConnection != null)
            {
                // The device has an available audio playback connection. 
                playbackConnection.StateChanged += this.AudioPlaybackConnection_ConnectionStateChanged;
                this.audioPlaybackConnections.Add(selectedDeviceId, playbackConnection);
                await playbackConnection.StartAsync();
                OpenAudioPlaybackConnectionButtonButton.IsEnabled = true;
            }
        }
    }
}

オーディオ再生接続を開くOpen the audio playback connection

前の手順では、オーディオ再生接続が作成されましたが、 Open または openasyncを呼び出すことによって接続が開かれるまで、サウンドの再生は開始されません。In the previous step, an audio playback connection was created, but sound does not begin playing until the connection is opened by calling Open or OpenAsync. [ オーディオ再生接続を開く ] ボタンをクリックし、[ハンドラー] をクリックして現在選択されているデバイスを取得し、ID を使用してアプリの接続の辞書から Audioplaybackconnection を取得します。In the Open Audio Playback Connection button click handler, get the currently selected device and use the ID to retrieve the AudioPlaybackConnection from the app's dictionary of connections. Openasyncへの呼び出しを待機し、返されたAudioplaybackconnectionopenresultstatusオブジェクトの状態の値を確認して、接続が正常に開かれたかどうかを確認し、存在する場合は接続状態のテキストボックスを更新します。Await a call to OpenAsync and check the Status value of the returned AudioPlaybackConnectionOpenResultStatus object to see if the connection was opened successfully and, if so, update the connection state text box.

private async void OpenAudioPlaybackConnectionButtonButton_Click(object sender, RoutedEventArgs e)
{
    var selectedDevice = (DeviceListView.SelectedItem as DeviceInformation).Id;
    AudioPlaybackConnection selectedConnection;

    if (this.audioPlaybackConnections.TryGetValue(selectedDevice, out selectedConnection))
    {
        if ((await selectedConnection.OpenAsync()).Status == AudioPlaybackConnectionOpenResultStatus.Success)
        {
            // Notify that the AudioPlaybackConnection is connected. 
            ConnectionState.Text = "Connected";
        }
        else
        {
            // Notify that the connection attempt did not succeed. 
            ConnectionState.Text = "Disconnected (attempt failed)";
        }
    }
}

オーディオ再生の接続状態を監視するMonitor audio playback connection state

接続の状態が変化するたびに、 ConnectionStateChanged イベントが発生します。The AudioPlaybackConnection.ConnectionStateChanged event is raised whenever the state of the connection changes. この例では、このイベントのハンドラーによって状態テキストボックスが更新されます。In this example, the handler for this event updates the status text box. Ui スレッドで更新が行われるようにするには、必ず、 Dispatcher の呼び出し内の ui を更新してください。Remember to update the UI inside a call to Dispatcher.RunAsync to make sure the update is made on the UI thread.

private async void AudioPlaybackConnection_ConnectionStateChanged(AudioPlaybackConnection sender, object args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        if (sender.State == AudioPlaybackConnectionState.Closed)
        {
            ConnectionState.Text = "Disconnected";
        }
        else if (sender.State == AudioPlaybackConnectionState.Opened)
        {
            ConnectionState.Text = "Connected";
        }
        else
        {
            ConnectionState.Text = "Unknown";
        }
    });
}

接続を解放し、削除されたデバイスを処理するRelease connections and handle removed devices

この例では、[ オーディオ再生接続のリリース ] ボタンを使用して、ユーザーがオーディオ再生接続を解放できるようにします。This example provides a Release Audio Playback Connection button to allow the user to release an audio playback connection. このイベントのハンドラーでは、現在選択されているデバイスを取得し、デバイスの ID を使用して、辞書内の Audioplaybackconnection を検索します。In the handler for this event, we get the currently selected device and use the device's ID to look up the AudioPlaybackConnection in the dictionary. Disposeを呼び出して参照を解放し、関連付けられているすべてのリソースを解放して、ディクショナリから接続を削除します。Call Dispose to release the reference and free any associated resources and remove the connection from the dictionary.


private void ReleaseAudioPlaybackConnectionButton_Click(object sender, RoutedEventArgs e)
{
    // Check if an audio playback connection was already created for the selected device Id. If it was then release its reference to deactivate it. 
    // The underlying transport is deactivated when all references are released. 
    if (!(DeviceListView.SelectedItem is null))
    {
        var selectedDeviceId = (DeviceListView.SelectedItem as DeviceInformation).Id;
        if (audioPlaybackConnections.ContainsKey(selectedDeviceId))
        {
            AudioPlaybackConnection connectionToRemove = audioPlaybackConnections[selectedDeviceId];
            connectionToRemove.Dispose();
            this.audioPlaybackConnections.Remove(selectedDeviceId);

            // Notify that the media device has been deactivated. 
            ConnectionState.Text = "Disconnected";
            OpenAudioPlaybackConnectionButtonButton.IsEnabled = false;
        }
    }
}

接続が有効になっているとき、または開いている間にデバイスが削除された場合は、そのケースを処理する必要があります。You should handle the case where a device is removed while a connection is enabled or open. これを行うには、デバイスウォッチャーの devicewatcher イベントのハンドラーを実装します。To do this, implement a handler for the device watcher's DeviceWatcher.Removed event. まず、削除されたデバイスの ID を使用して、アプリの ListView コントロールにバインドされている観測可能なコレクションからデバイスを削除します。First, the ID of the removed device is used to remove the device from the observable collection bound to the app's ListView control. 次に、このデバイスに関連付けられている接続がアプリのディクショナリ内にある場合は、 Dispose が呼び出されて、関連付けられているリソースが解放され、その後、接続がディクショナリから削除されます。Next, if a connection associated with this device is in the app's dictionary, Dispose is called to free the associated resources and then the connection is removed from the dictionary. これらはすべて、ui の更新が UI スレッドで実行されるようにするために、Dispatcher の呼び出し内で行われます。All of this is done within a call to Dispatcher.RunAsync to make sure the UI updates are performed on the UI thread.

private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
{
    // Collections bound to the UI are updated in the UI thread. 
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Find the device for the given id and remove it from the list. 
        foreach (DeviceInformation device in this.devices)
        {
            if (device.Id == deviceInfoUpdate.Id)
            {
                this.devices.Remove(device);
                break;
            }
        }

        if (audioPlaybackConnections.ContainsKey(deviceInfoUpdate.Id))
        {
            AudioPlaybackConnection connectionToRemove = audioPlaybackConnections[deviceInfoUpdate.Id];
            connectionToRemove.Dispose();
            this.audioPlaybackConnections.Remove(deviceInfoUpdate.Id);
        }
    });
}

メディアの再生Media Playback