音声出力の変更の処理

ユーザーは、オーディオ アプリでは音量を制御できて当然と考えています。音量コントロール(デバイスのボタンやノブ、UI のスライダー)を使用できたり、再生中に周辺機器(ヘッドフォンなど)を切断したときに突然大きな音が出るのを防いだりするのは、一般的な機能です。

音量コントロールの使用

ユーザーが音楽アプリやゲームアプリで音量キーを押したら、曲間でプレーヤーの再生が一時停止されていたり、ゲームの現在の場所には音楽がなかったりした場合でも、音量を変更するようにします。

Android では、音楽、アラーム、通知、着信音、システム音、通話音量、DTMF トーンの再生に、個別の音声ストリームが使用されます。これにより、ユーザーは各ストリームの音量を個別に制御できます。

デフォルトでは、音量コントロールを押すと、アクティブな音声ストリームの音量が変更されます。アプリで何も再生していない場合は、音量ボタンを押すと音楽(Android 9 より前では着信音)の音量が調整されます。

アラーム以外のアプリでは、用途を AudioAttributes.USAGE_MEDIA に指定して音声を再生します。

音量コントロールで正しいストリームを調整するには、setVolumeControlStream() を呼び出す際に、AudioAttributes.getVolumeControlStream から取得できる属性に一致するストリーム タイプを渡します。

Kotlin

    setVolumeControlStream(AudioManager.STREAM_MUSIC)
    

Java

    setVolumeControlStream(AudioManager.STREAM_MUSIC);
    

この呼び出しは、アプリのライフサイクルで、通常はメディアを制御するアクティビティまたはフラグメントの onResume() メソッドから行います。これにより、ターゲットのアクティビティまたはフラグメントが表示されるたびに、音量コントロールが STREAM_MUSIC に接続されます。

プログラムによるストリーム音量の制御

使う機会はまれですが、音声ストリームの音量をプログラムで設定できます。たとえば、アプリで既存の UI を置き換える場合などです。これは、Android AudioManager により同じタイプの音声ストリームがすべて 1 つにミックスされるため、推奨されません。以下のメソッドでは、そのストリームを使用するすべてのアプリの音量が変更されます。これらの使用は避けてください。

固定音量デバイスへの対応

一部のデバイス(Chromebooks など)では、音量コントロールはありますが、アプリで前述の AudioManager メソッドを使用して音声ストリームのレベルを変更することはできません。このようなデバイスを「固定音量」デバイスと呼びます。アプリが固定音量デバイスで実行されているかどうかを確認するには、isVolumeFixed() を呼び出します。

オーディオ アプリには、同じストリームで再生される他のアプリと出力音量のバランスを取る機能を付けるようにします。 固定音量デバイスで実行されるアプリでは、固有の音量コントロールを下表の適切な setVolume() メソッドに接続するようにします。

プレーヤー メソッド
AudioTrack AudioTrack.setVolume()
MediaPlayer MediaPlayer.setVolume()
ExoPlayer 基礎となる AudioTrack の音量を設定する SimpleExoPlayer.setVolume() を使用します。

突然の大音量再生の防止

ユーザーは、さまざまな方法で Android デバイスのオーディオを聴くことができます。ほとんどのデバイスは内蔵スピーカーと有線ヘッドセット用のヘッドフォン端子を備えており、Bluetooth 接続や A2DP オーディオをサポートするものも少なくありません。

ヘッドセットが取り外されたり、Bluetooth デバイスが切断されたりすると、音声ストリームは自動的に内蔵スピーカーにルート変更されます。大音量で音楽を聴いていた場合は、周囲を驚かせる可能性があります。

ユーザーは通常、オンスクリーンの再生コントロールを備えた音楽プレーヤーは、このような場合に再生を一時停止するものと考えています。その他のアプリ(コントロールを備えていないゲームなど)では、そのまま再生を続けます。ユーザーは、デバイスのハードウェア コントロールで音量を調節できます。

音声出力が内蔵スピーカーに戻ると、システムにより ACTION_AUDIO_BECOMING_NOISY インテントがブロードキャストされます。BroadcastReceiver を作成して、音声を再生するときは必ずこのインテントをリッスンするようにします。レシーバは次のようになります。

Kotlin

    private class BecomingNoisyReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {
                // Pause the playback
            }
        }
    }
    

Java

    private class BecomingNoisyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
          if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
              // Pause the playback
          }
        }
    }
    

このレシーバは、再生を開始するときに登録し、再生を停止するときに登録を解除します。このガイドの説明に沿ってアプリを設計する場合、これらの呼び出しはメディア セッション コールバック onPlay()onStop() で行います。

Kotlin

    private val intentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
    private val myNoisyAudioStreamReceiver = BecomingNoisyReceiver()

    private val callback = object : MediaSessionCompat.Callback() {

        override fun onPlay() {
            registerReceiver(myNoisyAudioStreamReceiver, intentFilter)
        }

        override fun onStop() {
            unregisterReceiver(myNoisyAudioStreamReceiver)
        }
    }
    

Java

    private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    private BecomingNoisyReceiver myNoisyAudioStreamReceiver = new BecomingNoisyReceiver();

    MediaSessionCompat.Callback callback = new
    MediaSessionCompat.Callback() {
      @Override
      public void onPlay() {
        registerReceiver(myNoisyAudioStreamReceiver, intentFilter);
      }

      @Override
      public void onStop() {
        unregisterReceiver(myNoisyAudioStreamReceiver);
      }
    }