minor fmod cleanup

This commit is contained in:
qimiko committed 2024-01-18 19:06:31 -07:00
1 parent d1e239037b
commit 6a11b76903
3 files changed
+75 -57

No files matched your search

+48 -20
View File
@@ -1,26 +1,32 @@
package org.fmod package org.fmod
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack import android.media.AudioTrack
import android.os.Build
import android.util.Log import android.util.Log
class AudioDevice { class AudioDevice {
private var mTrack: AudioTrack? = null private var mTrack: AudioTrack? = null
private fun fetchChannelConfigFromCount(i: Int): Int { private fun fetchChannelConfigFromCount(speakerCount: Int): Int {
if (i == 1) { return when (speakerCount) {
return 2 1 -> AudioFormat.CHANNEL_OUT_MONO
2 -> AudioFormat.CHANNEL_OUT_STEREO
6 -> AudioFormat.CHANNEL_OUT_5POINT1
8 -> AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
else -> AudioFormat.CHANNEL_INVALID
} }
if (i == 2) {
return 3
}
if (i == 6) {
return 252
}
return if (i == 8) 6396 else 0
} }
fun init(i: Int, i2: Int, i3: Int, i4: Int): Boolean { fun init(channelCount: Int, sampleRateInHz: Int, sampleSize: Int, sampleCount: Int): Boolean {
val fetchChannelConfigFromCount = fetchChannelConfigFromCount(i) val channelConfig = fetchChannelConfigFromCount(channelCount)
val minBufferSize = AudioTrack.getMinBufferSize(i2, fetchChannelConfigFromCount, 2) val minBufferSize = AudioTrack.getMinBufferSize(
sampleRateInHz,
channelConfig,
AudioFormat.ENCODING_PCM_16BIT
)
if (minBufferSize < 0) { if (minBufferSize < 0) {
Log.w( Log.w(
"fmod", "fmod",
@@ -29,11 +35,33 @@ class AudioDevice {
} else { } else {
Log.i("fmod", "AudioDevice::init : Min buffer size: $minBufferSize bytes") Log.i("fmod", "AudioDevice::init : Min buffer size: $minBufferSize bytes")
} }
val i5 = i3 * i4 * i * 2
val i6 = if (i5 > minBufferSize) i5 else minBufferSize val realBufferSize = sampleSize * sampleCount * channelCount * 2
Log.i("fmod", "AudioDevice::init : Actual buffer size: $i6 bytes") val bufferSize = minBufferSize.coerceAtLeast(realBufferSize)
Log.i("fmod", "AudioDevice::init : Actual buffer size: $bufferSize bytes")
return try { return try {
val audioTrack = AudioTrack(3, i2, fetchChannelConfigFromCount, 2, i6, 1) val attributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.build()
val format = AudioFormat.Builder()
.setChannelMask(channelConfig)
.setSampleRate(sampleRateInHz)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.build()
val audioTrackBuilder = AudioTrack.Builder()
.setAudioAttributes(attributes)
.setAudioFormat(format)
.setBufferSizeInBytes(bufferSize)
.setTransferMode(AudioTrack.MODE_STREAM)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioTrackBuilder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
}
val audioTrack = audioTrackBuilder.build()
mTrack = audioTrack mTrack = audioTrack
try { try {
audioTrack.play() audioTrack.play()
@@ -44,7 +72,7 @@ class AudioDevice {
mTrack = null mTrack = null
false false
} }
} catch (unused2: IllegalArgumentException) { } catch (_: IllegalArgumentException) {
Log.e("fmod", "AudioDevice::init : AudioTrack creation caused IllegalArgumentException") Log.e("fmod", "AudioDevice::init : AudioTrack creation caused IllegalArgumentException")
false false
} }
@@ -60,7 +88,7 @@ class AudioDevice {
mTrack = null mTrack = null
} }
fun write(sArr: ShortArray?, i: Int) { fun write(sArr: ShortArray, size: Int) {
mTrack!!.write(sArr!!, 0, i) mTrack?.write(sArr, 0, size)
} }
} }
+16 -28
View File
@@ -5,7 +5,9 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.res.AssetManager import android.content.res.AssetManager
import android.media.AudioDeviceInfo
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@@ -23,12 +25,10 @@ object FMOD {
@JvmStatic @JvmStatic
fun init(context: Context?) { fun init(context: Context?) {
gContext = context gContext = context
if (context != null) { gContext?.registerReceiver(
gContext!!.registerReceiver( gPluginBroadcastReceiver,
gPluginBroadcastReceiver, IntentFilter(Intent.ACTION_HEADSET_PLUG)
IntentFilter("android.intent.action.HEADSET_PLUG") )
)
}
} }
@JvmStatic @JvmStatic
@@ -58,29 +58,21 @@ object FMOD {
val isBluetoothOn = isBluetoothOn() val isBluetoothOn = isBluetoothOn()
Log.i( Log.i(
"fmod", "fmod",
"FMOD::supportsLowLatency : Low latency = $lowLatencyFlag, Pro Audio = $proAudioFlag, Bluetooth On = $isBluetoothOn, Acceptable Block Size = $z ($outputBlockSize)" "FMOD::supportsLowLatency : Low latency = $lowLatencyFlag, Pro Audio = $proAudioFlag, Bluetooth On = $isBluetoothOn, Acceptable Block Size = $z ($outputBlockSize)"
) )
return z && lowLatencyFlag && !isBluetoothOn return z && lowLatencyFlag && !isBluetoothOn
} }
@JvmStatic @JvmStatic
fun lowLatencyFlag(): Boolean { fun lowLatencyFlag(): Boolean {
if (Build.VERSION.SDK_INT < 5) {
return false
}
val context = gContext ?: return false val context = gContext ?: return false
return context.packageManager.hasSystemFeature("android.hardware.audio.low_latency") return context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)
} }
@JvmStatic @JvmStatic
fun proAudioFlag(): Boolean { fun proAudioFlag(): Boolean {
if (Build.VERSION.SDK_INT < 5) {
return false
}
val context = gContext ?: return false val context = gContext ?: return false
return context.packageManager.hasSystemFeature("android.hardware.audio.pro") return context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO)
} }
@JvmStatic @JvmStatic
@@ -90,26 +82,18 @@ object FMOD {
@JvmStatic @JvmStatic
fun getOutputSampleRate(): Int { fun getOutputSampleRate(): Int {
if (Build.VERSION.SDK_INT < 17) {
return 0
}
val context = gContext ?: return 0 val context = gContext ?: return 0
val audioService = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager val audioService = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val property = audioService.getProperty("android.media.property.OUTPUT_SAMPLE_RATE") val property = audioService.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
return property?.toInt() ?: 0 return property?.toInt() ?: 0
} }
@JvmStatic @JvmStatic
fun getOutputBlockSize(): Int { fun getOutputBlockSize(): Int {
if (Build.VERSION.SDK_INT < 17) {
return 0
}
val context = gContext ?: return 0 val context = gContext ?: return 0
val audioService = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager val audioService = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val property = audioService.getProperty("android.media.property.OUTPUT_FRAMES_PER_BUFFER") val property = audioService.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
return property?.toInt() ?: 0 return property?.toInt() ?: 0
} }
@@ -118,7 +102,11 @@ object FMOD {
fun isBluetoothOn(): Boolean { fun isBluetoothOn(): Boolean {
val context = gContext ?: return false val context = gContext ?: return false
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
return audioManager.isBluetoothA2dpOn || audioManager.isBluetoothScoOn
val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
return devices.any {
it.type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP || it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
}
} }
@JvmStatic @JvmStatic
+11 -9
View File
@@ -1,5 +1,6 @@
package org.fmod package org.fmod
import android.media.MediaCodec
import android.media.MediaCrypto import android.media.MediaCrypto
import android.media.MediaDataSource import android.media.MediaDataSource
import android.media.MediaExtractor import android.media.MediaExtractor
@@ -14,8 +15,7 @@ class MediaCodec {
var mCodecPtr: Long = 0 var mCodecPtr: Long = 0
private var mCurrentOutputBufferIndex = -1 private var mCurrentOutputBufferIndex = -1
private var mDataSourceProxy: Any? = null private var mDecoder: MediaCodec? = null
private var mDecoder: android.media.MediaCodec? = null
private var mExtractor: MediaExtractor? = null private var mExtractor: MediaExtractor? = null
private var mInputBuffers: Array<ByteBuffer>? = null private var mInputBuffers: Array<ByteBuffer>? = null
private var mInputFinished = false private var mInputFinished = false
@@ -57,7 +57,7 @@ class MediaCodec {
) )
if (string == "audio/mp4a-latm") { if (string == "audio/mp4a-latm") {
return try { return try {
mDecoder = android.media.MediaCodec.createDecoderByType(string) mDecoder = MediaCodec.createDecoderByType(string)
mExtractor!!.selectTrack(i2) mExtractor!!.selectTrack(i2)
mDecoder!!.configure(trackFormat, null as Surface?, null as MediaCrypto?, 0) mDecoder!!.configure(trackFormat, null as Surface?, null as MediaCrypto?, 0)
mDecoder!!.start() mDecoder!!.start()
@@ -100,7 +100,7 @@ class MediaCodec {
} }
} }
fun read(bArr: ByteArray?, i: Int): Int { fun read(bArr: ByteArray, i: Int): Int {
var dequeueInputBuffer = 0 var dequeueInputBuffer = 0
val i2 = val i2 =
if (!mInputFinished || !mOutputFinished || mCurrentOutputBufferIndex != -1) 0 else -1 if (!mInputFinished || !mOutputFinished || mCurrentOutputBufferIndex != -1) 0 else -1
@@ -129,14 +129,14 @@ class MediaCodec {
mCurrentOutputBufferIndex = dequeueOutputBuffer mCurrentOutputBufferIndex = dequeueOutputBuffer
mOutputBuffers!![dequeueOutputBuffer].limit(bufferInfo.size) mOutputBuffers!![dequeueOutputBuffer].limit(bufferInfo.size)
mOutputBuffers!![dequeueOutputBuffer].position(bufferInfo.offset) mOutputBuffers!![dequeueOutputBuffer].position(bufferInfo.offset)
} else if (dequeueOutputBuffer == -3) { } else if (dequeueOutputBuffer == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
mOutputBuffers = mDecoder!!.outputBuffers mOutputBuffers = mDecoder!!.outputBuffers
} else if (dequeueOutputBuffer == -2) { } else if (dequeueOutputBuffer == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
Log.d( Log.d(
"fmod", "fmod",
"MediaCodec::read : MediaCodec::dequeueOutputBuffer returned MediaCodec.INFO_OUTPUT_FORMAT_CHANGED " + mDecoder!!.outputFormat "MediaCodec::read : MediaCodec::dequeueOutputBuffer returned MediaCodec.INFO_OUTPUT_FORMAT_CHANGED " + mDecoder!!.outputFormat
) )
} else if (dequeueOutputBuffer == -1) { } else if (dequeueOutputBuffer == MediaCodec.INFO_TRY_AGAIN_LATER) {
Log.d( Log.d(
"fmod", "fmod",
"MediaCodec::read : MediaCodec::dequeueOutputBuffer returned MediaCodec.INFO_TRY_AGAIN_LATER." "MediaCodec::read : MediaCodec::dequeueOutputBuffer returned MediaCodec.INFO_TRY_AGAIN_LATER."
@@ -147,7 +147,7 @@ class MediaCodec {
"MediaCodec::read : MediaCodec::dequeueOutputBuffer returned $dequeueOutputBuffer" "MediaCodec::read : MediaCodec::dequeueOutputBuffer returned $dequeueOutputBuffer"
) )
} }
if (bufferInfo.flags and 4 !== 0) { if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
mOutputFinished = true mOutputFinished = true
} }
} }
@@ -188,13 +188,15 @@ class MediaCodec {
} }
val bArr = ByteArray(1024) val bArr = ByteArray(1024)
while (i3 > 0) { while (i3 > 0) {
i3 -= read(bArr, Math.min(1024, i3)) i3 -= read(bArr, 1024.coerceAtMost(i3))
} }
} }
companion object { companion object {
@JvmStatic
external fun fmodGetSize(j: Long): Long external fun fmodGetSize(j: Long): Long
@JvmStatic
external fun fmodReadAt(j: Long, j2: Long, bArr: ByteArray?, i: Int, i2: Int): Int external fun fmodReadAt(j: Long, j2: Long, bArr: ByteArray?, i: Int, i2: Int): Int
} }
} }