From 670a308c78cb2eec51c51b060f34e9f712436c21 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 11 Apr 2024 20:57:47 +0900 Subject: [PATCH] audiobank now returns samples in floats --- src/net/torvald/terrarum/audio/AudioBank.kt | 2 +- .../torvald/terrarum/audio/AudioProcessBuf.kt | 20 +++++------ .../terrarum/audio/MixerTrackProcessor.kt | 22 +++++++----- .../torvald/terrarum/audio/MusicContainer.kt | 35 +++++++++++++++---- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/net/torvald/terrarum/audio/AudioBank.kt b/src/net/torvald/terrarum/audio/AudioBank.kt index 5b360db4a..6920524ac 100644 --- a/src/net/torvald/terrarum/audio/AudioBank.kt +++ b/src/net/torvald/terrarum/audio/AudioBank.kt @@ -25,7 +25,7 @@ abstract class AudioBank : Disposable { abstract val totalSizeInSamples: Long abstract fun currentPositionInSamples(): Long - abstract fun readBytes(buffer: ByteArray): Int + abstract fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int abstract fun reset() abstract val songFinishedHook: (AudioBank) -> Unit diff --git a/src/net/torvald/terrarum/audio/AudioProcessBuf.kt b/src/net/torvald/terrarum/audio/AudioProcessBuf.kt index 79248d600..c16192cdd 100644 --- a/src/net/torvald/terrarum/audio/AudioProcessBuf.kt +++ b/src/net/torvald/terrarum/audio/AudioProcessBuf.kt @@ -25,7 +25,7 @@ private data class Frac(var nom: Int, val denom: Int) { * * Created by minjaesong on 2023-11-17. */ -class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray) -> Int?, val onAudioFinished: () -> Unit) { +class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (FloatArray, FloatArray) -> Int?, val onAudioFinished: () -> Unit) { var pitch: Float = 1f var playbackSpeed = 1f @@ -144,7 +144,8 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray) private val fmidR = FloatArray((fetchSize * 2 + 1.0).toInt()) private val foutL = FloatArray(internalBufferSize) // 640 for (44100, 48000), 512 for (48000, 48000) with BUFFER_SIZE = 512 * 4 private val foutR = FloatArray(internalBufferSize) // 640 for (44100, 48000), 512 for (48000, 48000) with BUFFER_SIZE = 512 * 4 - private val readBuf = ByteArray(fetchSize * 4) + private val readBufL = FloatArray(fetchSize) + private val readBufR = FloatArray(fetchSize) init { // printdbg(this, "App.audioMixerBufferSize=${App.audioBufferSize}") @@ -160,28 +161,26 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray) val readCount = if (samplesInBuf < App.audioBufferSize) fetchSize else 0 val writeCount = (readCount / q).roundToInt() - fun getFromReadBuf(i: Int, bytesRead: Int) = if (i < bytesRead) readBuf[i].toUint() else 0 + fun getFromReadBufL(index: Int, samplesRead: Int) = if (index < samplesRead) readBufL[index] else 0f + fun getFromReadBufR(index: Int, samplesRead: Int) = if (index < samplesRead) readBufR[index] else 0f if (readCount > 0) { try { shift(finL, readCount) shift(finR, readCount) - val bytesRead = audioReadFun(readBuf) + val samplesRead = audioReadFun(readBufL, readBufR) // printdbg(this, "Reading audio $readCount samples, got ${bytesRead?.div(4)} samples") - if (bytesRead == null || bytesRead <= 0) { + if (samplesRead == null || samplesRead <= 0) { // printdbg(this, "Music finished; bytesRead = $bytesRead") onAudioFinished() } else { for (c in 0 until readCount) { - val sl = (getFromReadBuf(4 * c + 0, bytesRead) or getFromReadBuf(4 * c + 1, bytesRead).shl(8)).toShort() - val sr = (getFromReadBuf(4 * c + 2, bytesRead) or getFromReadBuf(4 * c + 3, bytesRead).shl(8)).toShort() - - val fl = sl / 32767f - val fr = sr / 32767f + val fl = getFromReadBufL(c, samplesRead) + val fr = getFromReadBufR(c, samplesRead) finL[2 * PADSIZE + c] = fl finR[2 * PADSIZE + c] = fr @@ -217,6 +216,7 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray) // printdbg(this, "phase = $fPhaseL") } + // return the copy of the foutL/R fun getLR(): Pair { val samplesInBuf = validSamplesInBuf diff --git a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt index 948b164f5..c1043ad53 100644 --- a/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt +++ b/src/net/torvald/terrarum/audio/MixerTrackProcessor.kt @@ -88,29 +88,33 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) { printdbg("Allocating a StreamBuf with rate ${track.currentTrack!!.samplingRate}") - streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { buffer -> - var bytesRead = track.currentTrack?.readBytes(buffer) ?: 0 + streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { bufL, bufR -> + var samplesRead = track.currentTrack?.readSamples(bufL, bufR) ?: 0 // do gapless fetch if there is space in the buffer - if (track.doGaplessPlayback && bytesRead < buffer.size) { + if (track.doGaplessPlayback && samplesRead < bufL.size) { track.currentTrack?.reset() track.pullNextTrack() - bytesRead += read0(buffer, bytesRead) + samplesRead += read00(bufL, bufR, samplesRead) } - bytesRead + samplesRead }, { purgeStreamBuf() }).also { // it.jitterMode = jitterMode // it.jitterIntensity = jitterIntensity } } - private fun read0(buffer: ByteArray, bytesRead: Int): Int { - val tmpBuf = ByteArray(buffer.size - bytesRead) - val newRead = track.currentTrack?.readBytes(tmpBuf) ?: 0 + private fun read00(bufL: FloatArray, bufR: FloatArray, samplesRead: Int): Int { + val bufSize = bufL.size - samplesRead + val tmpBufL = FloatArray(bufSize) + val tmpBufR = FloatArray(bufSize) - System.arraycopy(tmpBuf, 0, buffer, bytesRead, tmpBuf.size) + val newRead = track.currentTrack?.readSamples(tmpBufL, tmpBufR) ?: 0 + + System.arraycopy(tmpBufL, 0, bufL, samplesRead, tmpBufL.size) + System.arraycopy(tmpBufR, 0, bufR, samplesRead, tmpBufR.size) return newRead } diff --git a/src/net/torvald/terrarum/audio/MusicContainer.kt b/src/net/torvald/terrarum/audio/MusicContainer.kt index 6d101b6dd..73ace8cc8 100644 --- a/src/net/torvald/terrarum/audio/MusicContainer.kt +++ b/src/net/torvald/terrarum/audio/MusicContainer.kt @@ -12,6 +12,7 @@ import javazoom.jl.decoder.Bitstream import net.torvald.reflection.extortField import net.torvald.reflection.forceInvoke import net.torvald.terrarum.App.printdbg +import net.torvald.terrarum.serialise.toUint import net.torvald.unsafe.UnsafeHelper import net.torvald.unsafe.UnsafePtr import java.io.File @@ -121,16 +122,38 @@ class MusicContainer( } } - private fun read0(buffer: ByteArray, bytesRead: Int): Int { - val tmpBuf = ByteArray(buffer.size - bytesRead) - val newRead = readBytes(tmpBuf) + override fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int { + assert(bufferL.size == bufferR.size) - System.arraycopy(tmpBuf, 0, buffer, bytesRead, tmpBuf.size) + val byteSize = bufferL.size * bytesPerSample + val bytesBuf = ByteArray(byteSize) + val bytesRead = readBytes(bytesBuf) + val samplesRead = bytesRead / bytesPerSample - return newRead + if (channels == 2) { + for (i in 0 until samplesRead) { + val sl = (bytesBuf[i * 4 + 0].toUint() or bytesBuf[i * 4 + 1].toUint().shl(8)).toShort() + val sr = (bytesBuf[i * 4 + 2].toUint() or bytesBuf[i * 4 + 3].toUint().shl(8)).toShort() + val fl = sl / 32767f + val fr = sr / 32767f + bufferL[i] = fl + bufferR[i] = fr + } + } + else if (channels == 1) { + for (i in 0 until samplesRead) { + val s = (bytesBuf[i * 2 + 0].toUint() or bytesBuf[i * 2 + 1].toUint().shl(8)).toShort() + val f = s / 32767f + bufferL[i] = f + bufferR[i] = f + } + } + else throw IllegalStateException("Unsupported channel count: $channels") + + return samplesRead } - override fun readBytes(buffer: ByteArray): Int { + private fun readBytes(buffer: ByteArray): Int { if (soundBuf == null) { val bytesRead = gdxMusic.forceInvoke("read", arrayOf(buffer)) ?: 0 samplesReadCount += bytesRead / bytesPerSample