wavなどの短い音データなどをandroidで再生させる場合にAudioTrackを使うと素早くできます。ゲームのワンフレーズ等に使えるでしょう。
2024.1.1
AudioTrack
Audioの再生にはこのAudioTrack以外にもMediaPlayerやSoundPoolがあります。
- SoundPool
- 再生可能なフォーマット:mp3, ogg, wav(非圧縮), etc.
- Audio Mixerのように複数の音源を重ねて再生できる
- 事前にデコードしてメモリに展開するので低レイテンシー
- 再生できるのは5秒程度
- 繰り返し再生
- 再生スピードを可変できる
- それぞれの音源に優先度をつけられる
- ゲームの効果音再生に適している
- MediaPlayer
- Supported media formatsおそらくこれらをサポート
- BGMとしてあるいは楽曲の再生用途で使う
- AudioTrack
- メモリに直接展開して再生するので低レイテンシー
- 細かい操作ができる(してあげないといけない)
AudioTrack.Builder
AudioTrack.Builder を使ってみたいと思います。AudioTrack.Builderにサンプルがありますから、それをそのまま使いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
val audioTrack = AudioTrack.Builder() .setAudioAttributes( AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build() ) .setAudioFormat( AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(samplingRate) .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) .build() ) |
そのままといっても、音源に合わせて設定しないといけませんが
AudioAttributes
最初にrawフォルダをres以下に作成します。適当なwavファイルをその res/raw/ 以下に置きます。多くの音声ファイルをフォルダ階層で管理したい場合はassetsに置きます。
ここでは、how_are_you というwavファイルを使ってみます。
AudioAttributes からAudioAttributesを選定します。
- AudioAttribute:音源は音声なのでこれらを設定
- setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
- setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
- AudioFormat:これらは音源ファイルを調べればわかります
- setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- setSampleRate(SamplingRate)
- setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
- ステレオ/モノラルの設定
sampling rate
サンプリングレートを調べるには、windowsなどのエクスプローラーでこの音声ファイルのプロパティを見ます。ビットレートが512kbpsであることが分かり、PCMは16bitなので、
512000bps/16bps = 32000 がサンプリングレートとなります。
Macの場合はファイルの情報を見ます。
サンプルコード
以上を踏まえて簡単なテストコードを書いてみました。
MainActivity.java
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 |
package com.example.kotlinaudiotrack import android.media.AudioAttributes import android.media.AudioFormat import android.media.AudioTrack import android.os.Bundle import android.util.Log import android.view.View import android.widget.Button import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import java.util.Locale class MainActivity : AppCompatActivity() { // hello_world.wav のサンプリングレート private val samplingRate: Int = 32000 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_main) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } // ボタンを設定 val button = findViewById<Button>(R.id.button) // リスナーをボタンに登録 // expression lambda button.setOnClickListener { _: View? -> wavPlay() } } private fun wavPlay() { var wavData: ByteArray try { resources.openRawResource(R.raw.how_are_you).use { input -> // wavを読み込む wavData = ByteArray(input.available()) // input.read(wavData) val readBytes = String.format( Locale.US, "read bytes = %d", input.read(wavData) ) // input.read(wavData)のwarning回避のためだけ Log.d("debug", readBytes) } } catch (e: Exception) { throw java.lang.RuntimeException(e) } // バッファサイズの計算 val bufSize = AudioTrack.getMinBufferSize( samplingRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT ) // AudioTrack.Builder API level 26より val audioTrack = AudioTrack.Builder() .setAudioAttributes( AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build() ) .setAudioFormat( AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(samplingRate) .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) .build() ) .setBufferSizeInBytes(bufSize) .build() // 再生 audioTrack.play() // ヘッダ44byteをオミット audioTrack.write(wavData, 44, wavData.size - 44) } } |
レイアウト、画像は特に必要ないですが気分です
activity_main.xml
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 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <!-- 画像はなくても大丈夫です <ImageView android:src="@drawable/img" android:scaleType="centerCrop" android:contentDescription="@string/description" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> --> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="40dp" android:text="@string/button" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.2" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.8" /> </androidx.constraintlayout.widget.ConstraintLayout> |
strings.xml
1 2 3 4 5 |
<resources> <string name="app_name">KotlinAudioTrack</string> <string name="button">Audio Play</string> <string name="description">Jenifier</string> </resources> |
サンプル動画
audioTrack.write(wavData, 44, wavData.length-44);
としているのは、wavファイルのヘッダ部分が44byteあるので、それがノイズ、プチッ音になるのを回避するためです。
それと注意しないといけないのが、このまま使うと他の処理、画面が止まったりします。それが都合が悪い場合はThreadを使って非同期にするなど対応が必要です。
- 関連ページ
- 簡単なMediaPlayerで音楽を再生
- WAV音声ファイルをAudioTrackで再生
- SoundPoolで効果音を鳴らす
Reference:
AudioTrack