/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (c) 2010 Thomas Martitz * * This program 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 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #define _SYSTEM_WITH_JNI /* for getJavaEnvironment */ #include #include "debug.h" #include "pcm.h" extern JNIEnv *env_ptr; /* infos about our pcm chunks */ static size_t pcm_data_size; static char *pcm_data_start; /* cache frequently called methods */ static jmethodID play_pause_method; static jmethodID stop_method; static jmethodID set_volume_method; static jobject RockboxPCM_instance; /* * transfer our raw data into a java array * * a bit of a monster functions, but it should cover all cases to overcome * the issue that the chunk size of the java layer and our pcm chunks are * differently sized * * afterall, it only copies the raw pcm data from pcm_data_start to * the passed byte[]-array * * it is called from the PositionMarker callback of AudioTrack **/ JNIEXPORT void JNICALL Java_org_rockbox_RockboxPCM_pcmSamplesToByteArray(JNIEnv *env, jobject this, jbyteArray arr) { (void)this; size_t len; size_t array_size = (*env)->GetArrayLength(env, arr); if (array_size > pcm_data_size) len = pcm_data_size; else len = array_size; (*env)->SetByteArrayRegion(env, arr, 0, len, pcm_data_start); if (array_size > pcm_data_size) { /* didn't have enough data for the array ? */ size_t remaining = array_size - pcm_data_size; size_t offset = len; retry: pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size); if (pcm_data_size == 0) { DEBUGF("out of data\n"); return; } if (remaining > pcm_data_size) { /* got too little data, get more ... */ (*env)->SetByteArrayRegion(env, arr, offset, pcm_data_size, pcm_data_start); /* advance in the java array by the amount we copied */ offset += pcm_data_size; /* we copied at least a bit */ remaining -= pcm_data_size; pcm_data_size = 0; /* let's get another buch of data and try again */ goto retry; } else (*env)->SetByteArrayRegion(env, arr, offset, remaining, pcm_data_start); len = remaining; } pcm_data_start += len; pcm_data_size -= len; } void pcm_play_lock(void) { } void pcm_play_unlock(void) { } void pcm_dma_apply_settings(void) { } void pcm_play_dma_start(const void *addr, size_t size) { pcm_data_start = (char*)addr; pcm_data_size = size; pcm_play_dma_pause(false); } void pcm_play_dma_stop(void) { /* NOTE: due to how pcm_play_get_more_callback() works, this is * possibly called from pcmSamplesToByteArray(), i.e. another thread. * => We need to discover the env_ptr */ JNIEnv* env = getJavaEnvironment(); (*env)->CallVoidMethod(env, RockboxPCM_instance, stop_method); } void pcm_play_dma_pause(bool pause) { (*env_ptr)->CallVoidMethod(env_ptr, RockboxPCM_instance, play_pause_method, (int)pause); } size_t pcm_get_bytes_waiting(void) { return pcm_data_size; } const void * pcm_play_dma_get_peak_buffer(int *count) { uintptr_t addr = (uintptr_t)pcm_data_start; *count = pcm_data_size / 4; return (void *)((addr + 3) & ~3); } void pcm_play_dma_init(void) { /* in order to have background music playing after leaving the activity, * we need to allocate the PCM object from the Rockbox thread (the Activity * runs in a separate thread because it would otherwise kill us when * stopping it) * * Luckily we only reference the PCM object from here, so it's safe (and * clean) to allocate it here **/ JNIEnv e = *env_ptr; /* get the class and its constructor */ jclass RockboxPCM_class = e->FindClass(env_ptr, "org/rockbox/RockboxPCM"); jmethodID constructor = e->GetMethodID(env_ptr, RockboxPCM_class, "", "()V"); /* instance = new RockboxPCM() */ RockboxPCM_instance = e->NewObject(env_ptr, RockboxPCM_class, constructor); /* cache needed methods */ play_pause_method = e->GetMethodID(env_ptr, RockboxPCM_class, "play_pause", "(Z)V"); set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V"); stop_method = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V"); /* get initial pcm data, if any */ pcm_play_get_more_callback((void*)&pcm_data_start, &pcm_data_size); } void pcm_postinit(void) { } void pcm_set_mixer_volume(int volume) { (*env_ptr)->CallVoidMethod(env_ptr, RockboxPCM_instance, set_volume_method, volume); } /* Due to limitations of default_event_handler(), parameters gets swallowed when * being posted with queue_broadcast(), so workaround this by caching the last * value. */ static int lastPostedVolume = -1; int hosted_get_volume(void) { return lastPostedVolume; } JNIEXPORT void JNICALL Java_org_rockbox_RockboxPCM_postVolumeChangedEvent(JNIEnv *env, jobject this, jint volume) { (void) env; (void) this; if (volume != lastPostedVolume) { lastPostedVolume = volume; queue_broadcast(SYS_VOLUME_CHANGED, 0); } }