Skip to content

Commit

Permalink
Fix/alsa wait (#1770)
Browse files Browse the repository at this point in the history
* fix: use `snd_pcm_drop` to stop sound immediately

* fix: add and remove sample use in `wait()`

previously, `wait()` was returnign immediately, but `stop()` was not
stopping properly (drain, instead of drop), so it had the impression of working.

* check number of frames left to play, and wait until less than one period worth of frames remaining

Fixes the issue where sounds where not properly waiting to play before
finishing. Would cause only a very small portion of the start of the
sound to play.

* lock modification to `availableSamples`
  • Loading branch information
cmhulbert authored Jul 5, 2023
1 parent b70656a commit eb62d05
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class SoundAudioStream(
}
} catch (e: CancellationException) {
// Do nothing
nas.stop()
params.onCancel?.invoke()
} finally {
nas.wait()
Expand Down
32 changes: 27 additions & 5 deletions korau/src/commonMain/kotlin/korlibs/audio/sound/backends/ALSA.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ALSAPlatformAudioOutput(
coroutineContext: CoroutineContext,
frequency: Int,
) : PlatformAudioOutput(coroutineContext, frequency) {
override var availableSamples: Int = 0
val channels = 2
private val lock = Lock()
val sdeque = AudioSamplesDeque(channels)
Expand All @@ -45,15 +46,19 @@ class ALSAPlatformAudioOutput(
}

override suspend fun add(samples: AudioSamples, offset: Int, size: Int) {
if (!ASound2.initialized) return super.add(samples, offset, size)
if (size == 0) return
if (!ASound2.initialized) return super.add(samples, offset, samples.totalSamples)
//if (!running) start()

if (!running) delay(10.milliseconds)

while (running && lock { sdeque.availableRead > 4 * 1024 }) {
delay(10.milliseconds)
}
lock { sdeque.write(samples, offset, size) }
lock {
availableSamples += samples.totalSamples
sdeque.write(samples, offset, samples.totalSamples)
}
}

override fun start() {
Expand All @@ -70,6 +75,9 @@ class ALSAPlatformAudioOutput(
}
//println("START!")
sdeque.clear()
lock {
availableSamples = 0
}

if (!ASound2.initialized) return

Expand Down Expand Up @@ -127,16 +135,26 @@ class ALSAPlatformAudioOutput(
buff[n * channels + ch] = (samples[ch, n] * rscale).toInt().toShort()
}
}
if (!running) break
val result = ASound2.snd_pcm_writei(pcm, buff, frames)
//println("result=$result")
if (result == -ASound2.EPIPE) {
ASound2.snd_pcm_prepare(pcm)
} else {
while (running && ASound2.snd_pcm_delay(pcm) > frames) {
blockingSleep(10.milliseconds)
}
}
lock {
availableSamples -= samples.totalSamples
}
}
} finally {
//println("COMPLETED: $pcm")
thread = null
// stop()
lock {
availableSamples = 0
}
}
}.also {
it.isDaemon = true
Expand All @@ -148,9 +166,11 @@ class ALSAPlatformAudioOutput(
running = false

if (pcm != 0L) {
ASound2.snd_pcm_drain(pcm)
ASound2.snd_pcm_drop(pcm)
ASound2.snd_pcm_close(pcm)
//println("ASound2.snd_pcm_close: ${pcm}")
lock {
availableSamples = 0
}
pcm = 0L
}
}
Expand Down Expand Up @@ -181,6 +201,8 @@ interface ASound2 {
fun snd_pcm_writei(pcm: Long, buffer: ShortArray, size: Int): Int = ERROR
fun snd_pcm_prepare(pcm: Long): Int = ERROR
fun snd_pcm_drain(pcm: Long): Int = ERROR
fun snd_pcm_drop(pcm: Long): Int = ERROR
fun snd_pcm_delay(pcm: Long): Int = ERROR
fun snd_pcm_close(pcm: Long): Int = ERROR

companion object : ASound2 by ASoundImpl {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,20 @@ actual object ASoundImpl : ASound2 {
return tempOut.getInt(0L)
}

override fun snd_pcm_delay(pcm: Long): Int {
val tempDelay = Memory(4).also { it.clear() }
A2.snd_pcm_delay(pcm.toCPointer(), tempDelay)
return tempDelay.getInt(0L)
}

override fun snd_pcm_writei(pcm: Long, buffer: ShortArray, size: Int): Int {
val mem = Memory((buffer.size * 2).toLong()).also { it.clear() }
for (n in 0 until buffer.size) mem.setShort((n * 2).toLong(), buffer[n])
return A2.snd_pcm_writei(pcm.toCPointer(), mem, size)
}

override fun snd_pcm_prepare(pcm: Long): Int = A2.snd_pcm_prepare(pcm.toCPointer())
override fun snd_pcm_drop(pcm: Long): Int = A2.snd_pcm_drop(pcm.toCPointer())
override fun snd_pcm_drain(pcm: Long): Int = A2.snd_pcm_drain(pcm.toCPointer())
override fun snd_pcm_close(pcm: Long): Int = A2.snd_pcm_close(pcm.toCPointer())
}
Expand All @@ -76,6 +83,8 @@ object A2 {
@JvmStatic external fun snd_pcm_writei(pcm: Pointer?, buffer: Pointer?, size: Int): Int
@JvmStatic external fun snd_pcm_prepare(pcm: Pointer?): Int
@JvmStatic external fun snd_pcm_drain(pcm: Pointer?): Int
@JvmStatic external fun snd_pcm_drop(pcm: Pointer?): Int
@JvmStatic external fun snd_pcm_delay(pcm: Pointer?, delay: Pointer?): Int
@JvmStatic external fun snd_pcm_close(pcm: Pointer?): Int

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,21 @@ actual object ASoundImpl : ASound2 {
}
}

override fun snd_pcm_delay(params: Long): Int {
memScoped {
val out = alloc<IntVar>()
A2.snd_pcm_delay(params.toCPointer(), out.ptr)
return out.value.toInt()
}
}

override fun snd_pcm_writei(pcm: Long, buffer: ShortArray, size: Int): Int = buffer.usePinned {
A2.snd_pcm_writei(pcm.toCPointer(), it.startAddressOf, size)
}

override fun snd_pcm_prepare(pcm: Long): Int = A2.snd_pcm_prepare(pcm.toCPointer())
override fun snd_pcm_drain(pcm: Long): Int = A2.snd_pcm_drain(pcm.toCPointer())
override fun snd_pcm_drop(pcm: Long): Int = A2.snd_pcm_drop(pcm.toCPointer())
override fun snd_pcm_close(pcm: Long): Int = A2.snd_pcm_close(pcm.toCPointer())
}

Expand All @@ -67,6 +76,8 @@ internal object A2 : DynamicLibrary("libasound.so.2") {
val snd_pcm_writei by func<(pcm: COpaquePointer?, buffer: COpaquePointer?, size: Int) -> Int>()
val snd_pcm_prepare by func<(pcm: COpaquePointer?) -> Int>()
val snd_pcm_drain by func<(pcm: COpaquePointer?) -> Int>()
val snd_pcm_drop by func<(pcm: COpaquePointer?) -> Int>()
val snd_pcm_delay by func<(pcm: COpaquePointer?, delay: COpaquePointer?) -> Int>()
val snd_pcm_close by func<(pcm: COpaquePointer?) -> Int>()

}

0 comments on commit eb62d05

Please sign in to comment.