音を制御する - AudioManager

端末で音楽を再生したり、音を鳴らしたり、音量を調整したり、音に関連する制御は、[`android.media.AudioManager`](http://developer.android.com/reference/android/media/AudioManager.html) が持っています。
このクラスは、`Context#getSystemService(String)`経由で取得することができるので、`Context`がのある場所でこのクラスを使うことができます。

このクラスを使うにあたっては、いくつかの用語を整理しておく必要があります。

# 用語の整理

## ストリーム

音は、特定のストリーム上に流し込まれ、最終的にスピーカーの出力に変換されます。この時、例えば、音楽と通知の音が同時に流れるような場面があることからもわかるように、音を流すためのストリームには複数のものがあり、これらを使い分けることに酔って、同時に様々な音を流すことを可能にしています。

通常、以下のストリームが用意されています。用途ごとに使い分けてね、という感じ。

- [STREAM_ALARM](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_ALARM)
  - アラーム音のためのストリーム
- [STREAM_DTMF](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_DTMF)
  - [DTMF 音](http://ja.wikipedia.org/wiki/DTMF)のためのストリーム
- [STREAM_MUSIC](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_MUSIC)
  - 音楽再生用のストリーム
- [STREAM_NOTIFICATION](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_NOTIFICATION)
  - 通知音のためのストリーム
- [STREAM_RING](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_RING)
  - 電話の呼出音のためのストリーム
- [STREAM_SYSTEM](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_SYSTEM)
  - システム音のためのストリーム
- [STREAM_VOICE_CALL](http://developer.android.com/reference/android/media/AudioManager.html#STREAM_VOICE_CALL)
  - 通話音のためのストリーム

若干紛らわしい物もありますが、以上のような用途ごとにストリームが用意されています。

`AudioManager#NUM_STREAMS`で、隠されたものも含め存在する全ストリームの数が定義されていますが、`@Deprecated`のため使用は避けましょう。一方で、代わりに`AudioSystem#getNumStreamTypes()` を使うよう指示がありますが、肝心の`AudioSystem`クラスは`@hide`のためリファレンスにも存在しませんし勿論普通にはプログラムから参照することすらできません。南無。
頑張ってリフレクションでアクセスしても良いかもしれません。

## オーディオフォーカス

一方で、音楽を再生している最中に通知の音がなるのは邪魔だったり、通話中に他の音が流れてくるのは避けたいというようなケースも有ります。つまり、同時に 1 つのストリームからのみ音を流したい、という状況です。これを実現するのがオーディオフォーカスです。

アプリケーションが特定のストリームに対しオーディオフォーカスを取得すると、他のストリームの音は停止するか、またはボリュームが下がるかのどちらかになります。逆に、他のアプリケーションから見ると、オーディオフォーカスを他のアプリから奪われることになります。Android のシステムは、フォーカスの取得と剥奪に関して、ボリュームの調整や再生・停止のコントロールまではしないので、オーディオフォーカスがどこにあるかを見て、各アプリケーションが適切にボリュームのコントロールや再生・停止状態の管理をする必要があります。

オーディオフォーカスにも種類があります。

- [AUDIOFOCUS_GAIN](http://developer.android.com/reference/android/media/AudioManager.html#AUDIOFOCUS_GAIN)
  - フォーカスを取得する最も基本のタイプです。概ねフォーカスを破棄するタイミングが不定の場合にこのタイプでフォーカスを取得します。
- [AUDIOFOCUS_GAIN_TRANSIENT](http://developer.android.com/reference/android/media/AudioManager.html#AUDIOFOCUS_GAIN_TRANSIENT)
  - 一時的なフォーカスを取得するためのものです。短時間でフォーカスを破棄するタイプで、通知音やボイスナレーションの再生等で用いられます。
- [AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE](http://developer.android.com/reference/android/media/AudioManager.html#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
  - 一時的なフォーカスを取得するためのもので、基本的には `AUDIOFOCUS_GAIN_TRANSIENT` と同じですが、他のアプリケーションは音を再生できず、システムですらも音を鳴らすことを許可されなくなります。
- [AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK](http://developer.android.com/reference/android/media/AudioManager.html#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
  - 一時的なフォーカスを取得するためのもので、基本的には `AUDIOFOCUS_GAIN_TRANSIENT` と同じですが、他のアプリケーションはボリュームを落として音を鳴らすことを許可されます。音楽の再生を止めずに、ボリュームだけを下げたい場合に使います。

オーディオフォーカスを得ると、基本的には、ボリュームのコントロールの権限もフォーカスを取得したアプリケーションに対して、指定したストリームの音量を調整するよう渡されます。つまり、`STREAM_MUSIC` でフォーカスを取得すると、ボリュームキーによる音量のコントロールは `STREAM_MUSIC` に対して行われるようになります。

ただし、`AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK` はボリュームコントロールの権限をフォーカスを取得したアプリケーションには渡しません。よって、バックグラウンドで音楽を再生している最中に `AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK` で `STREAM_NOTIFICATION` に対するフォーカスを取得しても、ボリュームのコントロールは `STREAM_MUSIC` に対して行われます。

## ボリュームのレベル

ボリュームは、一定の段階の値を以ってコントロールしています。最高段階がいくつになるのかは端末に依存します。0 から最高段階まで、順にレベルが上がっていくごとに、ボリュームも大きくなります。

また、ボリュームはストリームごとに調整が可能です。

# 機能

## 音量の調整

音量の調整方法には 2 種類の方法があります。

### 上げる・下げる・戻す

ボリュームキーの操作をするような感覚で、上げる・下げる、という操作ができます。
[`AudioManager#adjustStreamVolume(int, int, int)`](http://developer.android.com/reference/android/media/AudioManager.html#adjustStreamVolume(int, int, int)) に、調整したいストリームと、調整したい方向の定数を渡します。

最後の引数は、ボリュームの調整をした時のフィードバックをどうするか決めるフラグです。ボリュームキーを押した時に表示されるような UI を表示したり、ボリュームの調整をしたことを示すフィードバック音を鳴らしたり、ヴァイブレーションをしたり、といったことが細かく制御できます。

### 特定のレベルに調整する

ボリュームを特定のレベルに直接設定することもできます。
[`AudioManager#setSreamVolume(int, int, int)`](http://developer.android.com/reference/android/media/AudioManager.html#setStreamVolume(int, int, int)) に、調整したいストリームと、レベル値を入力します。

## オーディオフォーカスの取得と破棄

オーディオフォーカスを得るには、[`AudioManager#requestAudioFocus(OnAudioFocusChangeListener, int, int)`](http://developer.android.com/reference/android/media/AudioManager.html#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int)) を使い、どのストリームに対するフォーカスを得たいかを支持します。`request`なので、失敗することもあります。

一旦オーディオフォーカスを得たならば、不要になった時に破棄をする必要があります。これは[`AudioManager#abandonAudioFocus(OnAudioFocusChangeListener)`](http://developer.android.com/reference/android/media/AudioManager.html#abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener))で行います。