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 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

View File

@@ -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<FloatArray, FloatArray> {
val samplesInBuf = validSamplesInBuf

View File

@@ -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
}

View File

@@ -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<Int>("read", arrayOf(buffer)) ?: 0
samplesReadCount += bytesRead / bytesPerSample