audiobank now returns samples in floats

This commit is contained in:
minjaesong
2024-04-11 20:57:47 +09:00
parent 3e12966e84
commit 670a308c78
4 changed files with 53 additions and 26 deletions

View File

@@ -25,7 +25,7 @@ abstract class AudioBank : Disposable {
abstract val totalSizeInSamples: Long abstract val totalSizeInSamples: Long
abstract fun currentPositionInSamples(): Long abstract fun currentPositionInSamples(): Long
abstract fun readBytes(buffer: ByteArray): Int abstract fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int
abstract fun reset() abstract fun reset()
abstract val songFinishedHook: (AudioBank) -> Unit abstract val songFinishedHook: (AudioBank) -> Unit

View File

@@ -25,7 +25,7 @@ private data class Frac(var nom: Int, val denom: Int) {
* *
* Created by minjaesong on 2023-11-17. * 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 pitch: Float = 1f
var playbackSpeed = 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 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 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 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 { init {
// printdbg(this, "App.audioMixerBufferSize=${App.audioBufferSize}") // 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 readCount = if (samplesInBuf < App.audioBufferSize) fetchSize else 0
val writeCount = (readCount / q).roundToInt() 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) { if (readCount > 0) {
try { try {
shift(finL, readCount) shift(finL, readCount)
shift(finR, readCount) shift(finR, readCount)
val bytesRead = audioReadFun(readBuf) val samplesRead = audioReadFun(readBufL, readBufR)
// printdbg(this, "Reading audio $readCount samples, got ${bytesRead?.div(4)} samples") // 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") // printdbg(this, "Music finished; bytesRead = $bytesRead")
onAudioFinished() onAudioFinished()
} }
else { else {
for (c in 0 until readCount) { for (c in 0 until readCount) {
val sl = (getFromReadBuf(4 * c + 0, bytesRead) or getFromReadBuf(4 * c + 1, bytesRead).shl(8)).toShort() val fl = getFromReadBufL(c, samplesRead)
val sr = (getFromReadBuf(4 * c + 2, bytesRead) or getFromReadBuf(4 * c + 3, bytesRead).shl(8)).toShort() val fr = getFromReadBufR(c, samplesRead)
val fl = sl / 32767f
val fr = sr / 32767f
finL[2 * PADSIZE + c] = fl finL[2 * PADSIZE + c] = fl
finR[2 * PADSIZE + c] = fr finR[2 * PADSIZE + c] = fr
@@ -217,6 +216,7 @@ class AudioProcessBuf(val inputSamplingRate: Int, val audioReadFun: (ByteArray)
// printdbg(this, "phase = $fPhaseL") // printdbg(this, "phase = $fPhaseL")
} }
// return the copy of the foutL/R
fun getLR(): Pair<FloatArray, FloatArray> { fun getLR(): Pair<FloatArray, FloatArray> {
val samplesInBuf = validSamplesInBuf val samplesInBuf = validSamplesInBuf

View File

@@ -88,29 +88,33 @@ class MixerTrackProcessor(bufferSize: Int, val rate: Int, val track: TerrarumAud
private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) { private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) {
printdbg("Allocating a StreamBuf with rate ${track.currentTrack!!.samplingRate}") printdbg("Allocating a StreamBuf with rate ${track.currentTrack!!.samplingRate}")
streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { buffer -> streamBuf = AudioProcessBuf(track.currentTrack!!.samplingRate, { bufL, bufR ->
var bytesRead = track.currentTrack?.readBytes(buffer) ?: 0 var samplesRead = track.currentTrack?.readSamples(bufL, bufR) ?: 0
// do gapless fetch if there is space in the buffer // 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.currentTrack?.reset()
track.pullNextTrack() track.pullNextTrack()
bytesRead += read0(buffer, bytesRead) samplesRead += read00(bufL, bufR, samplesRead)
} }
bytesRead samplesRead
}, { purgeStreamBuf() }).also { }, { purgeStreamBuf() }).also {
// it.jitterMode = jitterMode // it.jitterMode = jitterMode
// it.jitterIntensity = jitterIntensity // it.jitterIntensity = jitterIntensity
} }
} }
private fun read0(buffer: ByteArray, bytesRead: Int): Int { private fun read00(bufL: FloatArray, bufR: FloatArray, samplesRead: Int): Int {
val tmpBuf = ByteArray(buffer.size - bytesRead) val bufSize = bufL.size - samplesRead
val newRead = track.currentTrack?.readBytes(tmpBuf) ?: 0 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 return newRead
} }

View File

@@ -12,6 +12,7 @@ import javazoom.jl.decoder.Bitstream
import net.torvald.reflection.extortField import net.torvald.reflection.extortField
import net.torvald.reflection.forceInvoke import net.torvald.reflection.forceInvoke
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.serialise.toUint
import net.torvald.unsafe.UnsafeHelper import net.torvald.unsafe.UnsafeHelper
import net.torvald.unsafe.UnsafePtr import net.torvald.unsafe.UnsafePtr
import java.io.File import java.io.File
@@ -121,16 +122,38 @@ class MusicContainer(
} }
} }
private fun read0(buffer: ByteArray, bytesRead: Int): Int { override fun readSamples(bufferL: FloatArray, bufferR: FloatArray): Int {
val tmpBuf = ByteArray(buffer.size - bytesRead) assert(bufferL.size == bufferR.size)
val newRead = readBytes(tmpBuf)
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) { if (soundBuf == null) {
val bytesRead = gdxMusic.forceInvoke<Int>("read", arrayOf(buffer)) ?: 0 val bytesRead = gdxMusic.forceInvoke<Int>("read", arrayOf(buffer)) ?: 0
samplesReadCount += bytesRead / bytesPerSample samplesReadCount += bytesRead / bytesPerSample