| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <string.h> |
| |
| #include "log.h" |
| #include "pcm.h" |
| #include "system_java.h" |
| |
| struct PcmDeviceStruct { |
| JNIEnv *env; |
| jobject device; |
| }; |
| |
| static jclass pcmDeviceClass = NULL; |
| |
| static int |
| findPcmDeviceClass (JNIEnv *env) { |
| return findJavaClass(env, &pcmDeviceClass, JAVA_OBJ_BRLTTY("PcmDevice")); |
| } |
| |
| PcmDevice * |
| openPcmDevice (int errorLevel, const char *device) { |
| PcmDevice *pcm = malloc(sizeof(*pcm)); |
| |
| if (pcm) { |
| memset(pcm, 0, sizeof(*pcm)); |
| pcm->env = getJavaNativeInterface(); |
| pcm->device = NULL; |
| |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID constructor = 0; |
| |
| if (findJavaConstructor(pcm->env, &constructor, pcmDeviceClass, |
| JAVA_SIG_CONSTRUCTOR())) { |
| jobject localReference = (*pcm->env)->NewObject(pcm->env, pcmDeviceClass, constructor); |
| |
| if (!clearJavaException(pcm->env, 1)) { |
| jobject globalReference = (*pcm->env)->NewGlobalRef(pcm->env, localReference); |
| |
| (*pcm->env)->DeleteLocalRef(pcm->env, localReference); |
| localReference = NULL; |
| |
| if (globalReference) { |
| pcm->device = globalReference; |
| return pcm; |
| } else { |
| logMallocError(); |
| clearJavaException(pcm->env, 0); |
| } |
| } |
| } |
| } |
| |
| free(pcm); |
| } else { |
| logMallocError(); |
| } |
| |
| return NULL; |
| } |
| |
| void |
| closePcmDevice (PcmDevice *pcm) { |
| if (pcm) { |
| if (pcm->device) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "close", |
| JAVA_SIG_METHOD(JAVA_SIG_VOID, |
| ))) { |
| (*pcm->env)->CallVoidMethod(pcm->env, pcm->device, method); |
| clearJavaException(pcm->env, 1); |
| } |
| } |
| |
| (*pcm->env)->DeleteGlobalRef(pcm->env, pcm->device); |
| } |
| |
| free(pcm); |
| } |
| } |
| |
| int |
| writePcmData (PcmDevice *pcm, const unsigned char *buffer, int count) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "write", |
| JAVA_SIG_METHOD(JAVA_SIG_BOOLEAN, |
| JAVA_SIG_ARRAY(JAVA_SIG_SHORT) // samples |
| ))) { |
| jint size = count / 2; |
| jshortArray jSamples = (*pcm->env)->NewShortArray(pcm->env, size); |
| |
| if (jSamples) { |
| typedef union { |
| const unsigned char *bytes; |
| const int16_t *actual; |
| } Samples; |
| |
| Samples samples = { |
| .bytes = buffer |
| }; |
| |
| (*pcm->env)->SetShortArrayRegion( |
| pcm->env, jSamples, 0, size, samples.actual |
| ); |
| |
| if (!clearJavaException(pcm->env, 1)) { |
| jboolean result = (*pcm->env)->CallBooleanMethod( |
| pcm->env, pcm->device, method, jSamples |
| ); |
| |
| (*pcm->env)->DeleteLocalRef(pcm->env, jSamples); |
| jSamples = NULL; |
| |
| if (!clearJavaException(pcm->env, 1)) { |
| if (result == JNI_TRUE) { |
| return 1; |
| } |
| } |
| } |
| } else { |
| logMallocError(); |
| clearJavaException(pcm->env, 0); |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| getPcmBlockSize (PcmDevice *pcm) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "getBufferSize", |
| JAVA_SIG_METHOD(JAVA_SIG_INT, |
| ))) { |
| jint result = (*pcm->env)->CallIntMethod(pcm->env, pcm->device, method); |
| if (!clearJavaException(pcm->env, 1)) return result; |
| } |
| } |
| |
| return 0X100; |
| } |
| |
| int |
| getPcmSampleRate (PcmDevice *pcm) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "getSampleRate", |
| JAVA_SIG_METHOD(JAVA_SIG_INT, |
| ))) { |
| jint result = (*pcm->env)->CallIntMethod(pcm->env, pcm->device, method); |
| if (!clearJavaException(pcm->env, 1)) return result; |
| } |
| } |
| |
| return 8000; |
| } |
| |
| int |
| setPcmSampleRate (PcmDevice *pcm, int rate) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "setSampleRate", |
| JAVA_SIG_METHOD(JAVA_SIG_VOID, |
| JAVA_SIG_INT // rate |
| ))) { |
| (*pcm->env)->CallVoidMethod(pcm->env, pcm->device, method, rate); |
| clearJavaException(pcm->env, 1); |
| } |
| } |
| |
| return getPcmSampleRate(pcm); |
| } |
| |
| int |
| getPcmChannelCount (PcmDevice *pcm) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "getChannelCount", |
| JAVA_SIG_METHOD(JAVA_SIG_INT, |
| ))) { |
| jint result = (*pcm->env)->CallIntMethod(pcm->env, pcm->device, method); |
| if (!clearJavaException(pcm->env, 1)) return result; |
| } |
| } |
| |
| return 1; |
| } |
| |
| int |
| setPcmChannelCount (PcmDevice *pcm, int channels) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "setChannelCount", |
| JAVA_SIG_METHOD(JAVA_SIG_VOID, |
| JAVA_SIG_INT // count |
| ))) { |
| (*pcm->env)->CallVoidMethod(pcm->env, pcm->device, method, channels); |
| clearJavaException(pcm->env, 1); |
| } |
| } |
| |
| return getPcmChannelCount(pcm); |
| } |
| |
| PcmAmplitudeFormat |
| getPcmAmplitudeFormat (PcmDevice *pcm) { |
| return PCM_FMT_S16N; |
| } |
| |
| PcmAmplitudeFormat |
| setPcmAmplitudeFormat (PcmDevice *pcm, PcmAmplitudeFormat format) { |
| return getPcmAmplitudeFormat(pcm); |
| } |
| |
| void |
| pushPcmOutput (PcmDevice *pcm) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "push", |
| JAVA_SIG_METHOD(JAVA_SIG_VOID, |
| ))) { |
| (*pcm->env)->CallVoidMethod(pcm->env, pcm->device, method); |
| clearJavaException(pcm->env, 1); |
| } |
| } |
| } |
| |
| void |
| awaitPcmOutput (PcmDevice *pcm) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "await", |
| JAVA_SIG_METHOD(JAVA_SIG_VOID, |
| ))) { |
| (*pcm->env)->CallVoidMethod(pcm->env, pcm->device, method); |
| clearJavaException(pcm->env, 1); |
| } |
| } |
| } |
| |
| void |
| cancelPcmOutput (PcmDevice *pcm) { |
| if (findPcmDeviceClass(pcm->env)) { |
| static jmethodID method = 0; |
| |
| if (findJavaInstanceMethod(pcm->env, &method, pcmDeviceClass, "cancel", |
| JAVA_SIG_METHOD(JAVA_SIG_VOID, |
| ))) { |
| (*pcm->env)->CallVoidMethod(pcm->env, pcm->device, method); |
| clearJavaException(pcm->env, 1); |
| } |
| } |
| } |