◆PROCESSING 逆引きリファレンス
カテゴリー:スマホ(AndroidMode)
効果音を鳴らすには(AndroidMode編)
【概要】
PROCESSINGにAndroidMode を導入する事で、PROCESSINGで開発したプログラムをAndroid端末上で動かす事ができるようになります。
AndroidModeの導入については「PROCESSINGをAndroid端末で動かすには(4.0版)」記事を参照してください。
PROCESSINGで音楽や効果音を演奏するには、minim と呼ばれる外部ライブラリーを使うのが便利です。が、このminim・・・残念なことにAndroidMode では動作しません。
AndroidModeで音楽や効果音を演奏するには、Android SDK が提供している命令を使う事になります。※
- 効果音:SoundPool を利用する
- BGM:MediaPlayer を利用する
※この他にも JetPlayer クラスや AudioTrack クラスを使う方法があります。
鳴らしたい音が音楽(BGMなど)なのか効果音なのかにより、利用する命令が異なります。
BGMを再生する方法については「音楽を演奏するには(AndroidMode編)」を参照して下さい。
MediaPlayer | SoundPool | |
---|---|---|
再生遅延 | あり(遅い)。利用時Loading | 少ない(早い)。事前Loading |
長い音 | OK | 10秒程度まで。もしくは1M byte以下のファイル? |
同時に扱えるデータ数 | 4個まで? | 256個まで |
代表的な対応フォーマット | AAC、mp4、flac、mp3、mkv、wav、ogg など | 同左。ただし ogg が推奨らしい |
対応フォーマットについては、以下の公式ドキュメントを参照してください。
またSoundPool は Android 5.0 (Lollipop)以降、利用方法が変更になりました。古い書籍や、検索でヒットする古い記事のコーディングには注意してください。
SoundPoolの使い方については、以下の記事などが参考となりました。ありがとうございます。
- nyanのアプリ開発 様: [Android] SoundPool で効果音を鳴らしてみる
- INDETAIL 様 : AndroidでSoundPoolを使っての音声再生の注意点
- Qiita 様 :【Android】ロリポップからのSoundPool @totem2048 さん
【詳細】
再生準備
AudioAttributes.Builder builder = builder . setUsage( int usage ) ;
AudioAttributes.Builder builder = builder . setContentType( int contentType ) ;
AudioAttributes attributes = attribute . build( ) ;
builder : AudioAttributesインスタンス
usage : 利用用途
contentType : 種別
attributes : 属性
再生準備を行うのに4つも命令を続けて書くのは面倒くさいので、通常は以下のように1行で記述します。
1 2 3 4 |
AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); |
usage と contentType は AudioAttributes クラスに定数として定義されているものを利用します。
以下はusage と contentType に与える値の代表例です。この他にも与える事が可能なパラメータがあります。詳しくは 公式サイト を参照してください。
Usage | |
---|---|
USAGE_ALARM(4) | ウェイクアップアラームなどのときに使用する |
USAGE_GAME(14) | ゲームオーディオの場合に使用する |
USAGE_MEDIA(1) | 音楽や映画のサウンドトラックなどの場合に使用する |
USAGE_NOTIFICATION(5) | 通知音の場合に使用する |
USAGE_UNKNOWN(0) | 不明な場合に使用する |
contentType | |
---|---|
CONTENT_TYPE_MOVIE(3) | 映画やテレビ番組のサウンドトラックに使用する |
CONTENT_TYPE_MUSIC(2) | 音楽の場合に使用する |
CONTENT_TYPE_SONIFICATION(4) | ビープ音や短い合成音の場合に使用する |
CONTENT_TYPE_SPEECH(1) | 音声の場合に使用する |
CONTENT_TYPE_UNKNOWN(0) | 不明な場合に使用する |
デフォルト値は、両方共 UNKNOWN になります。
SoundPool.Builder builder = builder.setAudioAttributes( AudioAttributes attributes );
SoundPool.Builder builder = builder . setMaxStreams ( int max ) ;
SoundPool soundPool = builder . build ( ) ;
builder : SoundPool.Builderインスタンス
soundPool : SoundPoolインスタンス
attributes : AudioAttributes インスタンス
max : ストリーム数(1-256)
再生準備を行ったら、続いてSoundPoolを作成します。これも沢山命令を書くのは面倒くさいので、通常は以下のように1行で記述します。
1 2 3 4 |
SoundPool soundPool = new SoundPool.Builder() .setAudioAttributes(attributes) .setMaxStreams(1) .build(); |
ストリーム数は、同時に扱う効果音の数です。作成するアプリケーションに応じて、MAX256までの値を指定します。
attributes には、先程作成した属性を与えます。
サウンドファイルを読み込むint sid = soundPool . load ( Context context, int resId, int priority ) ;
int sid = soundPool . load ( AssetFileDescriptor afd, int priority ) ;
context : アプリケーションコンテキスト
resId : リソースID
priority : 優先度。1固定
sid : サウンド識別子
afd : AssetManagerでopenFd したファイル識別子
SoundPoolクラスには、様々な読み込みメソッドが提供されています。
サウンドファイルをリソース(main¥res¥raw)に配置している場合は、以下のような感じで読み込みます。
1 2 3 4 5 |
Activity act = getActivity(); Context con = act.getApplicationContext(); //効果音を読み込む(res\raw から取得する) int soundCat = soundPool.load(con, R.raw.cat, 1); |
アセット(main¥Assets)に配置している場合は、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Activity act = getActivity(); //アセットから効果音を読み込む AssetManager assetManager = act.getResources().getAssets(); try{ AssetFileDescriptor fd = assetManager.openFd("cat.ogg"); } catch( IOException e){ //例外 throw new IllegalStateException( MessageFormat.format( "assetManager openFd error: msg={0}, value={1}", e.getMessage(), e.getStackTrace())); } int soundCat = soundPool.load(fd, 1); |
どちらも、実行するとサウンドの再生や停止などの制御に使うサウンド識別子が戻ってきます。
ただし load メソッドは非同期実行される事に注意してください。つまりサウンドファイルの読み込みが終了する前に、制御がアプリケーションに戻ってきます。
完全に読み込みが終了する前に再生などを行うと、音がでません。
サウンドファイルの読み込みが終了したか否かは、SoundPool.OnLoadCompleteListener を実装したクラスのonLoadCompleteメソッドに通知されてきます。
Load完了通知を受け取るvoid soundPool . setOnLoadCompleteListener ( SoundPool.OnLoadCompleteListener listener ) ;
soundPool : SoundPoolインスタンス
listener : 通知を受け取るリスナークラス
SoundPool.OnLoadCompleteListener を実装したクラスのインスタンスを作成し、該当インスタンスを setOnLoadCompleteListenerで定義します。
専用のクラスを作成しても良いのですが、面倒くさいので無名クラスなどを用いると良いでしょう。
1 2 3 4 5 6 |
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() { @Override public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { Log.d(TAG, "読み込み終了 sid=" + sampleId + ", 結果=" + status ); } }); |
こんな感じです。
sampleId には 読み込みを行ったサウンド識別子が戻されます。status が 0 ならロード成功、0以外ならロード失敗です。
なお効果音は短いデータである事が多いため、リスナーの登録は load 命令を実行する前に行いましょう。
極端に短い効果音などの場合は、リスナー設定前に読み込みが完了してしまい、リスナーが反応しない事が考えられるためです。
再生する
再生するint streamId = soundPool . play ( int sid, float leftVolume, float rightVolume, int priority, int loop, float rate ) ;
soundPool : SoundPoolインスタンス
streamId : ストリームID
sid : サウンド識別子
leftVolume : 左ボリューム(0.0 – 1.0)
rightVolume : 右ボリューム(0.0 – 1.0)
priority : 優先度(0 =最低優先度)
loop : ループモード(0 =ループなし、-1 =永遠にループ)
rate : 再生速度(1.0 =通常再生、範囲0.5〜2.0)
playメソッドで再生します。再生が始まると0以外のストリームIDが戻されます。
loopは再生回数です。-1なら永遠に繰り返し再生します。1なら2回、2なら3回演奏します。
ボリュームは左右独立で制御できます。0.0 が無音。1.0 が最大音量です。
rate は再生速度です。1.0 が通常速度での再生、2.0 なら倍速再生になります。
停止する
停止するvoid soundPool . stop( int streamID );
soundPool : SoundPoolインスタンス
streamId : ストリームID
streamId には、play メソッドから戻されたストリームIDを渡します。
ボリュームを変更する
ボリュームを変更void soundPool . setVolume( int streamID, float leftVolume, float rightVolume );
soundPool : SoundPoolインスタンス
streamId : ストリームID
leftVolume : 左ボリューム(0.0 – 1.0)
rightVolume : 右ボリューム(0.0 – 1.0)
streamId には、play メソッドから戻されたストリームIDを渡します。
開放する
開放するvoid soundPool . release ( ) ;
soundPool : SoundPoolインスタンス
作成したSoundPool インスタンスは、アプリケーション終了時に開放する必要があります。
PROCESSINGの場合なら、フラグメントの onDestroy() メソッドをオーバライドして、そこに開放処理を記述するのが良いでしょう。
【関連記事】
サンプルプログラム
効果音を再生する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
import android.app.Activity; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.media.AudioAttributes; import android.media.SoundPool; import java.io.IOException; import java.text.MessageFormat; import java.util.Timer; import java.util.TimerTask; /** * Android Mode SoundPool Sample * @author MSLABO * @version 1.0 */ final int SOUND_POOL_MAX = 1; //効果音数 int soundCat; //猫の鳴き声ID SoundPool soundPool = null; //効果音クラス AssetManager assetManager; //Assetマネージャ AssetFileDescriptor fd = null; //Asset FD Activity act; //Activity Context con; //Context boolean animationFlg; //アニメーションFLG boolean isReadSoundFlg; //効果音READ完了FLG PImage neko1, neko2; //ネコ娘の絵 /** * フラグメント終了時にSoundPoolを開放する */ @Override public void onDestroy() { if( soundPool != null ){ soundPool.release(); soundPool = null; } super.onDestroy(); } public void setup() { fullScreen(); act = getActivity(); con = act.getApplicationContext(); //画像読み込み neko1 = loadImage( "nekomusu1.png" ); neko2 = loadImage( "nekomusu2.png" ); //アセットから効果音を読み込む assetManager = act.getResources().getAssets(); try{ fd = assetManager.openFd("cat.ogg"); } catch( IOException e){ //例外 throw new IllegalStateException( MessageFormat.format( "assetManager openFd error: msg={0}, value={1}", e.getMessage(), e.getStackTrace())); } //効果音の再生準備をする AudioAttributes audioAttributes = new AudioAttributes .Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); soundPool = new SoundPool.Builder() .setAudioAttributes(audioAttributes) .setMaxStreams(SOUND_POOL_MAX) .build(); //効果音READ完了FLGを「未読み込み」にする isReadSoundFlg = false; //効果音の読み取りを確認するリスナー登録 soundPool.setOnLoadCompleteListener( new SoundPool.OnLoadCompleteListener() { @Override public void onLoadComplete( SoundPool soundPool,int sampleId,int status){ try{ if( status == 0 ){ //読み取り成功 isReadSoundFlg = true; } //Asset FD を閉じる fd.close(); } catch( IOException e){ //例外 throw new IllegalStateException( MessageFormat.format( "assetManager close error: msg={0}, value={1}", e.getMessage(), e.getStackTrace())); } } }); //アセットからの読み取り実行 soundCat = soundPool.load(fd, 1); //アニメーションFLGを初期化 animationFlg = false; } public void draw() { background(200); //画像を表示する int dispX = (width - neko1.width)/2; int dispY = (height - neko1.height)/2; //アニメーションFLG に応じて絵を切替える if( animationFlg == false ){ image( neko1, dispX,dispY); } else { image( neko2, dispX,dispY); } } /** * タッチイベント * ネコ娘を150ms間ウィンクさせ、鳴き声を出す */ @Override public boolean surfaceTouchEvent(MotionEvent motionEvent) { super.surfaceTouchEvent(motionEvent); if( motionEvent.getActionMasked() != MotionEvent.ACTION_DOWN ){ return true; } //アニメーションを切り替える if (animationFlg == false) { animationFlg = true; //効果音の準備ができていたら、鳴き声を出す if (isReadSoundFlg) { soundPool.play(soundCat, 1.0f, 1.0f, 0, 0, 1.0f); } //150ms間、ウィンクした画像を表示させる Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //150ms後、元の絵に戻す animationFlg = false; } }, 150); } return true; } |
Dataフォルダ(AndroidStudioの場合は Assetsフォルダ)に、鳴き声の効果音(cat.ogg)と、ネコ娘の絵(nekomusu1.png、nekomusu2.png)がある前提です。
画面をタップすると、ネコ娘がウィンクしながら鳴き声の効果音が鳴ります。
<出力サンプル>
(画像URL:illust-AC 様:シグ子さん、casa4さん)
効果音を再生する例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import android.app.Activity; import android.content.Context; import android.media.AudioAttributes; import android.media.SoundPool; import android.view.MotionEvent; import java.util.Timer; import java.util.TimerTask; import processing.core.PApplet; import processing.core.PImage; /** * Android Mode SoundPool Sample * @author MSLABO * @version 1.1 */ public class Sketch extends PApplet { final int SOUND_POOL_MAX = 1; //効果音数 int soundCat; //猫の鳴き声ID SoundPool soundPool; //効果音クラス Activity act; //Activity Context con; //Context boolean animationFlg; //アニメーションFLG boolean isReadSoundFlg; //効果音READ完了FLG PImage neko1, neko2; //ネコ娘の絵 @Override public void settings() { //Androidではフルスクリーンが推奨 fullScreen(); } /** * フラグメント終了時にSoundPoolを開放する */ @Override public void onDestroy() { if( soundPool != null ){ soundPool.release(); soundPool = null; } super.onDestroy(); } @Override public void setup() { fullScreen(); act = getActivity(); con = act.getApplicationContext(); //画像読み込み neko1 = loadImage( "nekomusu1.png" ); neko2 = loadImage( "nekomusu2.png" ); //効果音の再生準備をする AudioAttributes audioAttributes = new AudioAttributes .Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); soundPool = new SoundPool.Builder() .setAudioAttributes(audioAttributes) .setMaxStreams(SOUND_POOL_MAX) .build(); //効果音READ完了FLGを「未読み込み」にする isReadSoundFlg = false; //効果音の読み取りを確認するリスナー登録 soundPool.setOnLoadCompleteListener( new SoundPool.OnLoadCompleteListener() { @Override public void onLoadComplete( SoundPool soundPool, int sampleId, int status) { if( status == 0 ){ //読み取り成功 isReadSoundFlg = true; } } }); //効果音を読み込む(res\raw から取得する) soundCat = soundPool.load(con, R.raw.cat03, 1); //アニメーションFLGを初期化 animationFlg = false; } @Override public void draw() { background(200); //画像を表示する int dispX = (width - neko1.width)/2; int dispY = (height - neko1.height)/2; //アニメーションFLGに応じて絵を切替える if( animationFlg == false ){ image( neko1, dispX,dispY); } else { image( neko2, dispX,dispY); } } /** * タッチイベント * ネコ娘を150ms間ウィンクさせ、鳴き声を出す */ @Override public boolean surfaceTouchEvent(MotionEvent motionEvent) { super.surfaceTouchEvent(motionEvent); if( motionEvent.getActionMasked() != MotionEvent.ACTION_DOWN ){ return true; } //アニメーションを切り替える if (animationFlg == false) { animationFlg = true; //効果音の準備ができていたら、鳴き声を出す if (isReadSoundFlg) { soundPool.play(soundCat, 1.0f, 1.0f, 0, 0, 1.0f); } //150ms間、ウィンクした画像を表示させる Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //150ms後、元の絵に戻す animationFlg = false; } }, 150); } return true; } } |
AndroidStudio版のサンプルです。先程のサンプルと同じ例ですが、効果音ファイルをリソースIDで読み込んでいる箇所が異なります。
Assetsフォルダから読み込むのに比べ、余分な例外処理が無いのでスッキリしますね。ただしこの方法はPROCESSINGの標準エディタでは利用できません。
下記はサンプルプログラムと同じ動きになるように、 P5.js で書き直したものです。動作イメージを確認できます。
なお一部のスマホ搭載ブラウザからは正しく表示されない場合があります。その場合はPCから閲覧してください。
(画像URL:illust-AC 様:シグ子さん、効果音:On-Jin 音人 様)
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。