// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.
#include <stdlib.h>
#include "Library.h"
#include "choose_format.h"
#include "net_avadeaux_klipspringer_alsa_Alsa.h" // generated by javac -h
// -------------------------------------------------------------------------------------------------
// Helper functions used on errors and by free. The policy is that resources are freed on error in
// create, but in other cases the caller should catch IOException and call free.
// Throws exception and closes pcm. Returns 0 so it can be used with return.
static jlong
fail(JNIEnv *env, const char *message, snd_pcm_t *pcm, snd_pcm_hw_params_t *hw_params) {
raiseError(env, message);
if (hw_params != NULL) { snd_pcm_hw_params_free(hw_params); }
if (pcm) { snd_pcm_close(pcm); }
return 0;
}
// Conditionally attempts recovery after underrun/overrun.
static bool recover_xrun(JNIEnv *env, snd_pcm_t *pcm, int err, int attempts, int rem_attempts) {
if (err == -EPIPE && attempts) {
if (!rem_attempts) {
raiseIOException(env, "Too many attempts to recover from EPIPE");
return false;
}
if (snd_pcm_prepare(pcm) < 0) {
raiseIOException(env, "Cannot recover from ALSA overrun/underrun");
return false;
}
return true;
} else {
raiseErrorCodeException(env, err, snd_strerror(err));
return false;
}
}
// -------------------------------------------------------------------------------------------------
// Native Alsa methods
#define METHOD(name) JNICALL Java_net_avadeaux_klipspringer_alsa_Alsa_ ## name
// The alsaInit method is defined in AlsaDevice.c because it initializes static globals there.
JNIEXPORT jlong
METHOD(open) (JNIEnv *env,
jobject jthis,
jbyteArray jdevname,
jboolean input,
jint jbufferMicros,
jboolean jsigned,
jint jrate,
jint jbips,
jint jchannels,
jint jfs,
jboolean jbigend)
{
int err;
snd_pcm_t *pcm = NULL;
snd_pcm_hw_params_t *hw_params = NULL;
jbyte *devname = (*env)->GetByteArrayElements(env, jdevname, NULL);
if (devname == NULL) { return fail(env, "Failed to allocate device name string", pcm, hw_params); }
if (snd_pcm_hw_params_malloc(&hw_params)) { return fail(env, "Failed to allocate hardware params record", pcm, NULL); }
unsigned bufferMicros = jbufferMicros;
int dir = 0;
if ((err = snd_pcm_open(&pcm, (const char *) devname, input ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0)) < 0
|| (err = snd_pcm_hw_params_any(pcm, hw_params)) < 0
|| (err = snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0
|| (err = snd_pcm_hw_params_set_format(pcm, hw_params, get_format(jsigned, jbips, jfs/jchannels, jbigend))) < 0
|| (err = snd_pcm_hw_params_set_rate(pcm, hw_params, jrate, 0)) < 0
|| (err = snd_pcm_hw_params_set_channels(pcm, hw_params, jchannels)) < 0
|| (err = snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &bufferMicros, &dir)) < 0
|| (err = snd_pcm_hw_params(pcm, hw_params)) < 0
|| (err = snd_pcm_nonblock(pcm, 0)) < 0
|| (err = snd_pcm_prepare(pcm)) < 0) {
return fail(env, raiseErrorCodeException(env, err, snd_strerror(err)), pcm, hw_params);
}
snd_pcm_hw_params_free(hw_params);
if (input && (err = snd_pcm_start(pcm)) < 0) {
return fail(env, raiseErrorCodeException(env, err, snd_strerror(err)), pcm, NULL);
}
return (intptr_t) pcm;
}
JNIEXPORT void
METHOD(close) (JNIEnv *env,
jobject jthis,
jlong jpcm)
{
snd_pcm_close((snd_pcm_t *) (intptr_t) jpcm);
}
JNIEXPORT jint
METHOD(write) (JNIEnv *env,
jobject jthis,
jlong jpcm,
jobject jbbuf,
jint joff,
jint jframes,
jint jrecoAttempts)
{
void *buf = (*env)->GetDirectBufferAddress(env, jbbuf);
if (buf == NULL) { raiseError(env, "Failed to get buffer address"); return 0; }
snd_pcm_t *pcm = (snd_pcm_t *) (intptr_t) jpcm;
int ecount = jrecoAttempts;
while (true) {
jint n = snd_pcm_writei(pcm, ((char *) buf)+joff, jframes);
if (n >= 0) { return n; }
if (!recover_xrun(env, pcm, n, jrecoAttempts, ecount--)) { return 0; }
}
}
JNIEXPORT jint
METHOD(read) (JNIEnv *env,
jobject jthis,
jlong jpcm,
jobject jbbuf,
jint joff,
jint jframes,
jint jrecoAttempts)
{
void *buf = (*env)->GetDirectBufferAddress(env, jbbuf);
if (buf == NULL) { raiseError(env, "Failed to get buffer address"); return 0; }
snd_pcm_t *pcm = (snd_pcm_t *) (intptr_t) jpcm;
int ecount = jrecoAttempts;
while (true) {
jint n = snd_pcm_readi(pcm, ((char *) buf)+joff, jframes);
if (n >= 0) { return n; }
if (!recover_xrun(env, pcm, n, jrecoAttempts, ecount--)) { return 0; }
}
}
JNIEXPORT void
METHOD(pause) (JNIEnv *env,
jobject jthis,
jlong jpcm,
jint enable)
{
int err = snd_pcm_pause((snd_pcm_t *) (intptr_t) jpcm, enable);
if (err < 0) { raiseErrorCodeException(env, err, snd_strerror(err)); }
}
JNIEXPORT void
METHOD(drain) (JNIEnv *env,
jobject jthis,
jlong jpcm)
{
int err = snd_pcm_drain((snd_pcm_t *) (intptr_t) jpcm);
if (err < 0) { raiseErrorCodeException(env, err, snd_strerror(err)); }
}
JNIEXPORT jint
METHOD(availableFrames) (JNIEnv *env,
jobject jthis,
jlong jpcm,
jint jrecoAttempts)
{
snd_pcm_t *pcm = (snd_pcm_t *) (intptr_t) jpcm;
int ecount = jrecoAttempts;
while (true) {
jint a = snd_pcm_avail(pcm);
if (a >= 0) { return a; }
if (!recover_xrun(env, pcm, a, jrecoAttempts, ecount--)) { return 0; }
}
}
JNIEXPORT jint
METHOD(bufferFrames) (JNIEnv *env,
jobject jthis,
jlong jpcm)
{
snd_pcm_uframes_t ringbufsamples, persz;
int err = snd_pcm_get_params((snd_pcm_t *) (intptr_t) jpcm, &ringbufsamples, &persz);
if (err < 0) { raiseErrorCodeException(env, err, snd_strerror(err)); }
return ringbufsamples;
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home